<!DOCTYPE html>


  <html class="dark page-post">


<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>react知识点回顾 | Poetry&#39;s Blog</title>

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
    <meta name="keywords" content="JavaScript,react,">
  

  <meta name="description" content="来源于互联网  一、npm的配置 切换淘宝镜像源  npm config set registry https://registry.npm.taobao.orgnpm config get registrynpm install -g cnpm --registry=https://registry.npm.taobao.org  使用npm安装react  cnpm install reac">
<meta name="keywords" content="JavaScript,react">
<meta property="og:type" content="article">
<meta property="og:title" content="react知识点回顾">
<meta property="og:url" content="http://blog.poetries.top/2017/11/07/react-summary/index.html">
<meta property="og:site_name" content="Poetry&#39;s Blog">
<meta property="og:description" content="来源于互联网  一、npm的配置 切换淘宝镜像源  npm config set registry https://registry.npm.taobao.orgnpm config get registrynpm install -g cnpm --registry=https://registry.npm.taobao.org  使用npm安装react  cnpm install reac">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/470.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/471.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/472.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/473.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/474.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/475.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/478.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/479.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/480.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/481.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/482.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/477.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/476.png">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/483.png">
<meta property="og:updated_time" content="2020-08-15T04:25:31.934Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="react知识点回顾">
<meta name="twitter:description" content="来源于互联网  一、npm的配置 切换淘宝镜像源  npm config set registry https://registry.npm.taobao.orgnpm config get registrynpm install -g cnpm --registry=https://registry.npm.taobao.org  使用npm安装react  cnpm install reac">
<meta name="twitter:image" content="https://poetries1.gitee.io/img-repo/2019/10/470.png">

  

  
    <link rel="icon" href="/favicon.ico">
  

  <link href="/css/styles.css?v=c114cbeddx" rel="stylesheet">
<link href="/css/other.css?v=c114cbeddx" rel="stylesheet">


  
    <link rel="stylesheet" href="/css/personal-style.css">
  

  

  
  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?40b1f89aa80f2527b3db779c6898c879";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>


  
  <script type="text/javascript">
	(function(){
	    var bp = document.createElement('script');
	    var curProtocol = window.location.protocol.split(':')[0];
	    if (curProtocol === 'https') {
	        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
	    }
	    else {
	        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
	    }
	    var s = document.getElementsByTagName("script")[0];
	    s.parentNode.insertBefore(bp, s);
	})();
  </script>



  
    <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
  

  <!-- 聊天系统 -->
  
    
   <link type="text/css" rel="stylesheet" href="/renxi/default.css">
   <style>
      #modal {
        position: static !important;
      }
      .filter {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        background: #fe5757;
        animation: colorChange 30s ease-in-out infinite;
        animation-fill-mode: both;
        mix-blend-mode: overlay;
      }
  
      @keyframes colorChange {
        0%, 100% {
            opacity: 0;
        }
        50% {
            opacity: .9;
        }
      }
   </style>
</head>
</html>
<body>
  
  
    <span id="toolbox-mobile" class="toolbox-mobile">导航</span>
  

  <div class="post-header CENTER">
   
  <div class="toolbox">
    <a class="toolbox-entry" href="/">
      <span class="toolbox-entry-text">导航</span>
      <i class="icon-angle-down"></i>
      <i class="icon-home"></i>
    </a>
    <ul class="list-toolbox">
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/archives/"
            rel="noopener noreferrer"
            target="_self"
            >
            博客
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/categories/"
            rel="noopener noreferrer"
            target="_self"
            >
            分类
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/tags/"
            rel="noopener noreferrer"
            target="_self"
            >
            标签
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/search/"
            rel="noopener noreferrer"
            target="_self"
            >
            搜索
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/link/"
            rel="noopener noreferrer"
            target="_self"
            >
            友链
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/about/"
            rel="noopener noreferrer"
            target="_self"
            >
            关于
          </a>
        </li>
      
    </ul>
  </div>


</div>


  <div id="toc" class="toc-article">
    <strong class="toc-title">文章目录<i class="iconfont toc-title" style="display:inline-block;color:#87998d;width:20px;height:20px;">&#xf004b;</i></strong>
    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#一、npm的配置"><span class="toc-text">一、npm的配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#二、开发环境配置"><span class="toc-text">二、开发环境配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#三、认识JSX"><span class="toc-text">三、认识JSX</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-JSX-简介"><span class="toc-text">3.1 JSX 简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-JSX-属性"><span class="toc-text">3.2 JSX 属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-JSX-嵌套"><span class="toc-text">3.3 JSX 嵌套</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-4-JSX表达式"><span class="toc-text">3.4 JSX表达式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#四、组件类型"><span class="toc-text">四、组件类型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-函数定义与类定义组件"><span class="toc-text">4.1 函数定义与类定义组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-展示与容器组件"><span class="toc-text">4.2 展示与容器组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-有状态与无状态组件"><span class="toc-text">4.3 有状态与无状态组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-4-受控与非受控组件"><span class="toc-text">4.4 受控与非受控组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-5-组合与继承"><span class="toc-text">4.5 组合与继承</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#五、组件数据"><span class="toc-text">五、组件数据</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-props"><span class="toc-text">5.1 props</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-state"><span class="toc-text">5.2 state</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#六、组件生命周期"><span class="toc-text">六、组件生命周期</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#6-1-React是如何渲染组件的"><span class="toc-text">6.1 React是如何渲染组件的</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-2-React组件生命周期方法"><span class="toc-text">6.2 React组件生命周期方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-3-总结"><span class="toc-text">6.3 总结</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#七、表单及事件处理"><span class="toc-text">七、表单及事件处理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#7-1-表单"><span class="toc-text">7.1 表单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7-2-表单元素"><span class="toc-text">7.2 表单元素</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7-3-事件"><span class="toc-text">7.3 事件</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#八、redux-router"><span class="toc-text">八、redux-router</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#8-1、基本用法"><span class="toc-text">8.1、基本用法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-2、嵌套路由"><span class="toc-text">8.2、嵌套路由</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-3、-path-属性"><span class="toc-text">8.3、 path 属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-4、通配符"><span class="toc-text">8.4、通配符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-5、IndexRoute-组件"><span class="toc-text">8.5、IndexRoute 组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-6、Redirect-组件"><span class="toc-text">8.6、Redirect 组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-7、IndexRedirect-组件"><span class="toc-text">8.7、IndexRedirect 组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-8、Link"><span class="toc-text">8.8、Link</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-9、IndexLink"><span class="toc-text">8.9、IndexLink</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-10、histroy-属性"><span class="toc-text">8.10、histroy 属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-11、表单处理"><span class="toc-text">8.11、表单处理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-12、路由的钩子"><span class="toc-text">8.12、路由的钩子</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#九、redux"><span class="toc-text">九、redux</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#9-1-Redux-的适用场景"><span class="toc-text">9.1 Redux 的适用场景</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#9-2-基本概念和-API"><span class="toc-text">9.2 基本概念和 API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#9-3-Reducer-的拆分"><span class="toc-text">9.3 Reducer 的拆分</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#9-4-工作流程"><span class="toc-text">9.4 工作流程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#9-5-实例：计数器"><span class="toc-text">9.5 实例：计数器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#十、中间件与异步操作"><span class="toc-text">十、中间件与异步操作</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#10-1-中间件的概念"><span class="toc-text">10.1 中间件的概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#10-2-中间件的用法"><span class="toc-text">10.2 中间件的用法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#10-3、applyMiddlewares"><span class="toc-text">10.3、applyMiddlewares()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#10-4-异步操作的基本思路"><span class="toc-text">10.4 异步操作的基本思路</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#10-5-redux-thunk-中间件"><span class="toc-text">10.5 redux-thunk 中间件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#10-6、redux-promise-中间件"><span class="toc-text">10.6、redux-promise 中间件</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#十一、react-redux"><span class="toc-text">十一、react-redux</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#11-1-UI-组件"><span class="toc-text">11.1 UI 组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#11-2、容器组件"><span class="toc-text">11.2、容器组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#11-3、connect"><span class="toc-text">11.3、connect()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#11-4、mapStateToProps"><span class="toc-text">11.4、mapStateToProps()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#11-5、mapDispatchToProps"><span class="toc-text">11.5、mapDispatchToProps()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#11-6、-组件"><span class="toc-text">11.6、 组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#11-7、实例：计数器"><span class="toc-text">11.7、实例：计数器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#十二、思维导图总结"><span class="toc-text">十二、思维导图总结</span></a></li></ol>
  </div>
  




<div class="content content-post CENTER">
   <!-- canvas 彩带 -->
<canvas id="evanyou" width="1302" height="678" style="position: fixed;width: 100%;height: 100%;top: 0;left:0;z-index:-1;"></canvas>

<article id="post-react-summary" class="article article-type-post" itemprop="blogPost">
  <header class="article-header" style="position:relative;">
    <h1 class="post-title">react知识点回顾</h1>

    <div class="article-meta">
      <span>
        <i class="icon-calendar"></i>
        <span>2017.11.07</span>
      </span>

      
        <span class="article-author">
          <i class="icon-user"></i>
          <span>Poetry</span>
        </span>
      

      
  <span class="article-category">
    <i class="icon-list"></i>
    <a class="article-category-link" href="/categories/Front-End/">Front-End</a>
  </span>



      

      
      <i class="fa fa-eye"></i> 
        <span id="busuanzi_container_page_pv">
           &nbsp热度 <span id="busuanzi_value_page_pv">
           <i class="fa fa-spinner fa-spin"></i></span>℃
        </span>
      
      
       
          <span class="post-count">
            <i class="fa fa-file-word-o"></i>&nbsp
            <span>字数统计 11.4k字</span>
          </span>

          <span class="post-count">
            <i class="fa fa-columns"></i>&nbsp
            <span>阅读时长 48分</span>
          </span>
      
      
    </div>

    <i class="iconfont" id="toc-eye" style="display:inline-block;color:#b36619;position:absolute;top:0;right:0;cursor:pointer;
    font-size: 24px;">&#xe61c;</i>

  </header>

  <div class="article-content">
    
      <div id="container">
        <blockquote>
<p>来源于互联网</p>
</blockquote>
<h2 id="一、npm的配置"><a href="#一、npm的配置" class="headerlink" title="一、npm的配置"></a>一、npm的配置</h2><blockquote>
<p>切换淘宝镜像源</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">npm config <span class="keyword">set</span> registry https://registry.npm.taobao.org</span><br><span class="line"></span><br><span class="line">npm config <span class="keyword">get</span> registry</span><br><span class="line"></span><br><span class="line">npm install -g cnpm --registry=https://registry.npm.taobao.org</span><br></pre></td></tr></table></figure>
<blockquote>
<p>使用npm安装react</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">cnpm install react react-dom --save</span><br></pre></td></tr></table></figure>
<h2 id="二、开发环境配置"><a href="#二、开发环境配置" class="headerlink" title="二、开发环境配置"></a>二、开发环境配置</h2><blockquote>
<p>这里使用<code>create-react-app</code>初始化项目</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">npm install create-react-app -g</span><br></pre></td></tr></table></figure>
<blockquote>
<p>安装完成之后就可以在命令行使用 <code>create-react-app</code> 了，首先选择一个合适的目录，然后只需要简单地输入</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">create-react-app yourfilename</span><br></pre></td></tr></table></figure>
<h2 id="三、认识JSX"><a href="#三、认识JSX" class="headerlink" title="三、认识JSX"></a>三、认识JSX</h2><h3 id="3-1-JSX-简介"><a href="#3-1-JSX-简介" class="headerlink" title="3.1 JSX 简介"></a>3.1 JSX 简介</h3><blockquote>
<p><code>JSX</code> 其是一个语法扩展，它既不是单纯的字符串，也不是<code>HTML</code>，虽然长得和 <code>HTML</code> 很像甚至基本上看起来一样。但事实上它是 <code>React</code> 内部实现的一种，允许我们直接在 <code>JS</code> 里书写 <code>UI</code> 的方式</p>
</blockquote>
<h3 id="3-2-JSX-属性"><a href="#3-2-JSX-属性" class="headerlink" title="3.2 JSX 属性"></a>3.2 JSX 属性</h3><blockquote>
<p><code>JSX</code> 的标签同样可以拥有自己的属性</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> title = <span class="xml"><span class="tag">&lt;<span class="name">h1</span> <span class="attr">id</span>=<span class="string">"main"</span>&gt;</span>React Learning<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 注意是 className 而不是 class</span></span><br><span class="line"><span class="keyword">const</span> title = <span class="xml"><span class="tag">&lt;<span class="name">h1</span> <span class="attr">className</span>=<span class="string">"main"</span>&gt;</span>React Learning<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<h3 id="3-3-JSX-嵌套"><a href="#3-3-JSX-嵌套" class="headerlink" title="3.3 JSX 嵌套"></a>3.3 JSX 嵌套</h3><blockquote>
<p><code>JSX</code> 的标签也可以像 <code>HTML</code> 一样相互嵌套，一般有嵌套解构的 <code>JSX</code> 元素外面，我们习惯于为它加上一个小括号</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> title = (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">        &lt;h1 className=<span class="string">"main"</span>&gt;React Learning&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">        &lt;p&gt;Let's learn JSX&lt;/</span>p&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>需要注意的是，<code>JSX</code> 在嵌套时，最外层有且只能有一个标签，否则就会出错</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 这是一个错误示例</span></span><br><span class="line"><span class="keyword">const</span> title = (            </span><br><span class="line">    &lt;h1 className=<span class="string">"main"</span>&gt;React Learning&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">    &lt;p&gt;Let's learn JSX&lt;/</span>p&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<h3 id="3-4-JSX表达式"><a href="#3-4-JSX表达式" class="headerlink" title="3.4 JSX表达式"></a>3.4 JSX表达式</h3><blockquote>
<p>在 <code>JSX</code> 元素中，我们同样可以使用 <code>JavaScript</code> 表达式，在 <code>JSX</code> 当中的表达式需要用一个大括号括起来</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayhi</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">'Hi,'</span> + name</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> title = (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">        &lt;h1 className=<span class="string">"main"</span>&gt;React Learning&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">        &lt;p&gt;Let's learn JSX. &lt;span&gt;&#123;sayhi('you')&#125;&lt;/</span>span&gt;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>div&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<h2 id="四、组件类型"><a href="#四、组件类型" class="headerlink" title="四、组件类型"></a>四、组件类型</h2><h3 id="4-1-函数定义与类定义组件"><a href="#4-1-函数定义与类定义组件" class="headerlink" title="4.1 函数定义与类定义组件"></a>4.1 函数定义与类定义组件</h3><blockquote>
<p>第一种函数定义组件，非常简单啦，我们只需要定义一个接收<code>props</code>传值，返回<code>React</code>元素的方法即可</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Title</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello, &#123;props.name&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 甚至使用ES6的箭头函数简写之后可以变成这样</span></span><br><span class="line"><span class="keyword">const</span> Title = <span class="function"><span class="params">props</span> =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello, &#123;props.name&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>第二种是类定义组件，也就是使用<code>ES6</code>中新引入的类的概念来定义<code>React</code>组件</p>
</blockquote>
<ul>
<li>组件在定义好之后，可以通过<code>JSX</code>描述的方式被引用，组件之间也可以相互嵌套和组合</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Title</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello, &#123;this.props.name&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="4-2-展示与容器组件"><a href="#4-2-展示与容器组件" class="headerlink" title="4.2 展示与容器组件"></a>4.2 展示与容器组件</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 展示组件</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CommentList</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  renderComment(&#123;body, author&#125;) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">li</span>&gt;</span>&#123;body&#125;—&#123;author&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  render() &#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">ul</span>&gt;</span> &#123;this.props.comments.map(this.renderComment)&#125; <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span></span><br><span class="line">  &#125; </span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 容器组件</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CommentListContainer</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">    <span class="keyword">super</span>()</span><br><span class="line">    <span class="keyword">this</span>.state = &#123; <span class="attr">comments</span>: [] &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  componentDidMount() &#123;</span><br><span class="line">    $.ajax(&#123;</span><br><span class="line">      url: <span class="string">"/my-comments.json"</span>,</span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">      success: <span class="function"><span class="keyword">function</span>(<span class="params">comments</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.setState(&#123;<span class="attr">comments</span>: comments&#125;)</span><br><span class="line">      &#125;.bind(<span class="keyword">this</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">CommentList</span> <span class="attr">comments</span>=<span class="string">&#123;this.state.comments&#125;</span> /&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>展示组件</strong></p>
<ul>
<li>主要负责组件内容如何展示</li>
<li>从<code>props</code>接收父组件传递来的数据</li>
<li>大多数情况可以通过函数定义组件声明</li>
</ul>
<p><strong>容器组件</strong></p>
<ul>
<li>主要关注组件数据如何交互</li>
<li>拥有自身的<code>state</code>，从服务器获取数据，或与<code>redux</code>等其他数据处理模块协作</li>
<li>需要通过类定义组件声明，并包含生命周期函数和其他附加方法</li>
</ul>
<p><strong>那么这样写具体有什么好处呢？</strong></p>
<ul>
<li>解耦了界面和数据的逻辑</li>
<li>更好的可复用性，比如同一个回复列表展示组件可以套用不同数据源的容器组件</li>
<li>利于团队协作，一个人负责界面结构，一个人负责数据交互</li>
</ul>
<h3 id="4-3-有状态与无状态组件"><a href="#4-3-有状态与无状态组件" class="headerlink" title="4.3 有状态与无状态组件"></a>4.3 有状态与无状态组件</h3><p><strong>有状态组件</strong></p>
<blockquote>
<p>这个组件能够获取储存改变应用或组件本身的状态数据，在<code>React</code>当中也就是<code>state</code>，一些比较明显的特征是我们可以在这样的组件当中看到对<code>this.state</code>的初始化，或<code>this.setState</code>方法的调用</p>
</blockquote>
<p><strong>无状态组件</strong></p>
<blockquote>
<p>这样的组件一般只接收来自其他组件的数据。一般这样的组件中只能看到对<code>this.props</code>的调用</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 有状态组件</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StatefulLink</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">    <span class="keyword">this</span>.state = &#123;</span><br><span class="line">      active: <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  handleClick() &#123;</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">      active: !<span class="keyword">this</span>.state.active</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">a</span> </span></span></span><br><span class="line"><span class="xml">          style=&#123;&#123; color: this.state.active ? 'red' : 'black' &#125;&#125;</span></span><br><span class="line"><span class="xml">          onClick=&#123;this.handleClick.bind(this)&#125;</span></span><br><span class="line"><span class="xml">         &gt;</span></span><br><span class="line"><span class="xml">           Stateful Link</span></span><br><span class="line"><span class="xml">         <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 无状态组件</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StatelessLink</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">  &#125;</span><br><span class="line">  handleClick() &#123;</span><br><span class="line">    <span class="keyword">this</span>.props.handleClick(<span class="keyword">this</span>.props.router)</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> active = <span class="keyword">this</span>.props.activeRouter === <span class="keyword">this</span>.props.router</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        &lt;li&gt;</span><br><span class="line">            &lt;a </span><br><span class="line">              style=&#123;&#123; <span class="attr">color</span>: active ? <span class="string">'red'</span> : <span class="string">'black'</span> &#125;&#125;</span><br><span class="line">              onClick=&#123;<span class="keyword">this</span>.handleClick.bind(<span class="keyword">this</span>)&#125;</span><br><span class="line">             &gt;</span><br><span class="line">                Stateless Link</span><br><span class="line">            &lt;<span class="regexp">/a&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>li&gt;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在<code>React</code>的实际开发当中，我们编写的组件大部分都是无状态组件。毕竟<code>React</code>的主要作用是编写用户界面。再加上<code>ES6</code>的新特性，绝大多数的无状态组件都可以通过箭头函数简写成类似下面这样</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> SimpleButton = <span class="function"><span class="params">props</span> =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">button</span>&gt;</span>&#123;props.text&#125;<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<h3 id="4-4-受控与非受控组件"><a href="#4-4-受控与非受控组件" class="headerlink" title="4.4 受控与非受控组件"></a>4.4 受控与非受控组件</h3><p><strong>受控组件</strong></p>
<blockquote>
<p>比如说设置了<code>value</code>的<code>&lt;input&gt;</code> 是一个受控组件。对于受控的<code>&lt;input&gt;</code>，渲染出来的<code>html</code>元素始终保持着<code>value</code>属性的值，如以下代码</p>
</blockquote>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/470.png" alt></p>
<ul>
<li>此时如果想要更新用户的值。需要使用<code>onChange</code>事件</li>
</ul>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/471.png" alt></p>
<p><strong>非受控组件</strong></p>
<blockquote>
<p>即没有设置<code>value</code>或者设置为<code>null</code>的是一个非受控组件，对于非受控的<code>input</code>组件，用户的输入会直接反映在页面上</p>
</blockquote>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/472.png" alt></p>
<ul>
<li>上面的代码渲染出一个空值的输入框，用户的输入立即会反映在元素上</li>
<li>和受控组件一样，使用<code>onChange</code>事件来监听值的变化，如果想要给组件设置一个非空的初始值。可以使用<code>defaultValue</code>属</li>
<li>通常情况下，<code>React</code>当中所有的表单控件都需要是受控组件</li>
</ul>
<h3 id="4-5-组合与继承"><a href="#4-5-组合与继承" class="headerlink" title="4.5 组合与继承"></a>4.5 组合与继承</h3><ul>
<li><code>React</code>当中的组件是通过嵌套或组合的方式实现组件代码复用的</li>
<li>通过<code>props</code>传值和组合使用组件几乎可以满足所有场景下的需求。这样也更符合组件化的理念，就好像使用互相嵌套的<code>DOM</code>元素一样使用<code>React</code>的组件，并不需要引入继承的概念</li>
</ul>
<blockquote>
<p>继承的写法并不符合<code>React</code>的理念。在<code>React</code>当中<code>props</code>其实是非常强大的，<code>props</code>几乎可以传入任何东西，变量、函数、甚至是组件本身</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">SplitPane</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div className=<span class="string">"SplitPane"</span>&gt;</span><br><span class="line">      &lt;div className=<span class="string">"SplitPane-left"</span>&gt;</span><br><span class="line">        &#123;props.left&#125;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      &lt;div className="SplitPane-right"&gt;</span></span><br><span class="line"><span class="regexp">        &#123;props.right&#125;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>div&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  )</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">function App() &#123;</span></span><br><span class="line"><span class="regexp">  return (</span></span><br><span class="line"><span class="regexp">    &lt;SplitPane</span></span><br><span class="line"><span class="regexp">      left=&#123;</span></span><br><span class="line"><span class="regexp">        &lt;Contacts /</span>&gt;</span><br><span class="line">      &#125;</span><br><span class="line">      right=&#123;</span><br><span class="line">        &lt;Chat /&gt;</span><br><span class="line">      &#125; /&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>React官方也希望我们通过组合的方式来使用组件，如果你想实现一些非界面类型函数的复用，可以单独写在其他的模块当中在引入组件进行使用</p>
</blockquote>
<h2 id="五、组件数据"><a href="#五、组件数据" class="headerlink" title="五、组件数据"></a>五、组件数据</h2><hr>
<h3 id="5-1-props"><a href="#5-1-props" class="headerlink" title="5.1 props"></a>5.1 props</h3><ul>
<li>传入变量</li>
<li>传入函数</li>
<li>传入组件</li>
<li><code>props.children</code></li>
</ul>
<blockquote>
<ul>
<li>在形式上，<code>props</code>之于<code>JSX</code>就相当于<code>attributes</code>之于<code>HTML</code>。从写法上来看呢，我们为组件传入<code>props</code>就可以像为<code>HTML</code>标签添加属性一样</li>
<li>在概念上，props对于组件就相当于JS中参数之于函数。我们可以抽象出这样一个函数来解释</li>
</ul>
</blockquote>
<ul>
<li><code>props</code> 几乎可以传递所有的内容，包括变量、函数、甚至是组件本身</li>
</ul>
<p><strong>props是只读的</strong></p>
<ul>
<li>在<code>React</code>中，<code>props</code>都是自上向下传递，从父组件传入子组件</li>
<li>并且<code>props</code>是只读的，我们不能在组件中直接修改<code>props</code>的内容</li>
<li>也即是说组件只能根据传入的<code>props</code>渲染界面，而不能在其内部对<code>props</code>进行修改</li>
</ul>
<p><strong>props类型检查</strong></p>
<blockquote>
<p>正是因为<code>props</code>的强大，什么类型的内容都可以传递，所以在开发过程中，为了避免错误类型的内容传入，我们可以为<code>props</code>添加类型检查</p>
</blockquote>
<p><strong>props默认值</strong></p>
<blockquote>
<p>由于<code>props</code>是只读的，我们不能直接为<code>props</code>赋值。<code>React</code>专门准备了一个方法定义<code>props</code>的默认值</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> PropTypes <span class="keyword">from</span> <span class="string">'prop-types'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Title = <span class="function"><span class="params">props</span> =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>&#123;props.title&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line">Title.defaultProps = &#123;</span><br><span class="line">  title: <span class="string">'Wait for parent to pass props.'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Title.propTypes = &#123;</span><br><span class="line">  title: PropTypes.string.isRequired</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="5-2-state"><a href="#5-2-state" class="headerlink" title="5.2 state"></a>5.2 state</h3><ul>
<li>初始化</li>
<li><code>setState</code>方法</li>
<li>向下传递数据</li>
</ul>
<blockquote>
<ul>
<li>在<code>React</code>中<code>state</code>也是我们进行数据交互的地方，又或者叫做<code>state management</code>状态管理。</li>
<li>一个应用需要进行数据交互，比如同服务器之间的交互，同用户输入进行交互。话反过来，从<code>API</code>获取数据，处理用户输入也就是我们需要用到<code>state</code>的时候</li>
</ul>
</blockquote>
<ul>
<li>在新版本的<code>React</code>当中，我们通过类定义组件来声明一个有状态组件，之后在它的构造方法中初始化组件的<code>state</code>，我们可以先赋予它默认值。</li>
<li>之后就可以在组件中通过<code>this.state</code>来访问它，既然是<code>state</code>那么肯定涉及到数据的改变，因此我们还需额外定义一个负责处理<code>state</code>变化的函数，这样的函数中一般都会包含<code>this.setState</code>这个方法</li>
<li>和之前的<code>props</code>一样，初始化<code>state</code>之后，如果我们想改变它，是不可以直接对其赋值的，直接修改<code>state</code>的值没有任何意义，因为这样的操作脱离了<code>React</code>运行的逻辑，不会触发组件的重新渲染。所以需要<code>this.setState</code>这个方法，在改变<code>state</code>的同时，触发<code>React</code>内部的一系列函数，最后在页面上重新渲染出组件</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Counter</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">    <span class="keyword">this</span>.state = &#123;</span><br><span class="line">      counter: <span class="number">0</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  addOne() &#123;</span><br><span class="line">    <span class="keyword">this</span>.setState(<span class="function">(<span class="params">prevState</span>) =&gt;</span>(&#123;</span><br><span class="line">      counter: prevState.counter + <span class="number">1</span></span><br><span class="line">    &#125;))</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;p&gt;&#123; <span class="keyword">this</span>.state.counter &#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">        &lt;button</span></span><br><span class="line"><span class="regexp">          onClick=&#123;() =&gt; this.addOne()&#125;&gt;</span></span><br><span class="line"><span class="regexp">          Increment</span></span><br><span class="line"><span class="regexp">        &lt;/</span>button&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<h2 id="六、组件生命周期"><a href="#六、组件生命周期" class="headerlink" title="六、组件生命周期"></a>六、组件生命周期</h2><h3 id="6-1-React是如何渲染组件的"><a href="#6-1-React是如何渲染组件的" class="headerlink" title="6.1 React是如何渲染组件的"></a>6.1 React是如何渲染组件的</h3><blockquote>
<ul>
<li>在新版本的<code>React</code>当中，<code>React</code>的底层被重写了。<code>React</code>换上了一个新的引擎，这个引擎叫做<code>React Fiber.React Fiber</code> 作用的也即是<code>React</code>最核心的功能，它将<code>React</code>应用界面更新的过程分为了两个主要的部分：</li>
</ul>
</blockquote>
<ul>
<li>调度过程</li>
<li>执行过程</li>
</ul>
<blockquote>
<p>在调度过程中，有4个生命周期函数会被触发</p>
</blockquote>
<ul>
<li><code>componentWillMount</code></li>
<li><code>componentWillReceiveProps</code></li>
<li><code>shouldComponentUpdate</code></li>
<li><code>componentWillUpdate</code></li>
</ul>
<blockquote>
<p>在执行过程中，有3个生命周期函数会被触发：</p>
</blockquote>
<ul>
<li><code>componentDidMount</code></li>
<li><code>componentDidUpdate</code></li>
<li><code>componentWillUnmount</code></li>
</ul>
<h3 id="6-2-React组件生命周期方法"><a href="#6-2-React组件生命周期方法" class="headerlink" title="6.2 React组件生命周期方法"></a>6.2 React组件生命周期方法</h3><blockquote>
<p><code>React</code>为了方便我们更好地控制自己的应用，提供了许多预置的生命周期方法。这些固定的生命周期方法分别会在组件的挂载流程、更新流程、卸载流程中触发</p>
</blockquote>
<ul>
<li><code>componentWillMount</code> 开始插入真实DOM</li>
<li><code>componentDidMount</code> 插入真实<code>DOM</code>完成</li>
<li><code>componentWillUpdate</code> 开始重新渲染</li>
<li><code>componentDidUpdate</code> 重新渲染完成</li>
<li><code>componentWillUnmount</code>已移出真实 <code>DOM</code></li>
<li><code>componentWillReceiveProps</code> 已加载组件收到新的参数时调用</li>
<li><code>shouldComponentUpdate</code>组件判断是否重新渲染时调用</li>
</ul>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/473.png" alt><br><img src="https://poetries1.gitee.io/img-repo/2019/10/474.png" alt></p>
<p><strong>componentDidMount</strong></p>
<blockquote>
<p>在此方法中可进行</p>
</blockquote>
<ul>
<li>与其他 <code>JavaScript</code> 框架集成，如初始化 <code>jQuery</code> 插件；</li>
<li>使用 <code>setTimeout</code>/<code>setInterval</code> 设置定时器；</li>
<li>通过 <code>Ajax</code>/<code>Fetch</code> 获取数据；</li>
<li>绑定 <code>DOM</code> 事件</li>
</ul>
<h3 id="6-3-总结"><a href="#6-3-总结" class="headerlink" title="6.3 总结"></a>6.3 总结</h3><ul>
<li>React组件渲染包含三个流程：挂载流程、更新流程、卸载流程</li>
<li>各个生命周期函数会在特定的时刻触发并适用于不同的使用场景</li>
<li>通过使用生命周期函数我们可以对应用进行更精准的控制</li>
<li>如果你需要发起网络请求，将其安排在合适的生命周期函数中是值得推荐的做法</li>
<li>了解掌握<code>React</code>组件渲染的流程和原理对我们更深入掌握<code>React</code>非常有帮助</li>
</ul>
<h2 id="七、表单及事件处理"><a href="#七、表单及事件处理" class="headerlink" title="七、表单及事件处理"></a>七、表单及事件处理</h2><h3 id="7-1-表单"><a href="#7-1-表单" class="headerlink" title="7.1 表单"></a>7.1 表单</h3><blockquote>
<p>受控与非受控组件就是专门适用于React当中的表单元素的</p>
</blockquote>
<ul>
<li>只要是有表单出现的地方，就会有用户输入，就会有表单事件触发，就会涉及的数据处理</li>
<li>在我们用<code>React</code>开发应用时，为了更好地管理应用中的数据，响应用户的输入，编写组件的时候呢，我们就会运用到受控组件与非受控组件这两个概念。</li>
</ul>
<h3 id="7-2-表单元素"><a href="#7-2-表单元素" class="headerlink" title="7.2 表单元素"></a>7.2 表单元素</h3><blockquote>
<p>我们在组件中声明表单元素时，一般都要为表单元素传入应用状态中的值，可以通过<code>state</code>也可以通过<code>props</code>传递，之后需要为其绑定相关事件，例如表单提交，输入改变等。在相关事件触发的处理函数中，我们需要根据表单元素中用户的输入，对应用数据进行相应的操作和改变</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ControlledInput</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">    <span class="keyword">this</span>.state = &#123;</span><br><span class="line">      value: <span class="string">""</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  handleChange(event) &#123;</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">      value: event.target.value</span><br><span class="line">      &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">input</span> </span></span></span><br><span class="line"><span class="xml">              type="text" </span></span><br><span class="line"><span class="xml">              value=&#123;this.state.value&#125; </span></span><br><span class="line"><span class="xml">              onChange=&#123;() =&gt; this.handleChange()&#125; </span></span><br><span class="line"><span class="xml">            /&gt;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>受控组件的输入数据是一直和我们的应用状态绑定的，事件处理函数中一定要有关<code>state</code>的更新操作，这样表单组件才能及时正确响应用户的输入</p>
</blockquote>
<p><strong>textarea</strong></p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!--HTML--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">textarea</span>&gt;</span></span><br><span class="line">  Hello there, this is some text in a text area</span><br><span class="line"><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--jsx--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">textarea</span> <span class="attr">value</span>=<span class="string">&#123;this.state.value&#125;</span> <span class="attr">onChange</span>=<span class="string">&#123;this.handleChange&#125;</span> /&gt;</span></span><br></pre></td></tr></table></figure>
<p><strong>select</strong></p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!--HTML--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"grapefruit"</span>&gt;</span>Grapefruit<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"lime"</span>&gt;</span>Lime<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">option</span> <span class="attr">selected</span> <span class="attr">value</span>=<span class="string">"coconut"</span>&gt;</span>Coconut<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"mango"</span>&gt;</span>Mango<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--jsx--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">value</span>=<span class="string">&#123;this.state.value&#125;</span> <span class="attr">onChange</span>=<span class="string">&#123;this.handleChange&#125;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"grapefruit"</span>&gt;</span>Grapefruit<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"lime"</span>&gt;</span>Lime<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"coconut"</span>&gt;</span>Coconut<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">"mango"</span>&gt;</span>Mango<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h3 id="7-3-事件"><a href="#7-3-事件" class="headerlink" title="7.3 事件"></a>7.3 事件</h3><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!--HTML--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"activateLasers()"</span>&gt;</span></span><br><span class="line">  Activate Lasers</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--jsx--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;activateLasers&#125;</span>&gt;</span></span><br><span class="line">  Activate Lasers</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="八、redux-router"><a href="#八、redux-router" class="headerlink" title="八、redux-router"></a>八、redux-router</h2><p><img src="https://poetries1.gitee.io/img-repo/2019/10/475.png" alt></p>
<h3 id="8-1、基本用法"><a href="#8-1、基本用法" class="headerlink" title="8.1、基本用法"></a>8.1、基本用法</h3><blockquote>
<p>使用时，路由器<code>Router</code>就是<code>React</code>的一个组件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Router &#125; <span class="keyword">from</span> <span class="string">'react-router'</span>;</span><br><span class="line">render(<span class="xml"><span class="tag">&lt;<span class="name">Router</span>/&gt;</span></span>, <span class="built_in">document</span>.getElementById(<span class="string">'app'</span>));</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>Router</code>组件本身只是一个容器，真正的路由要通过<code>Route</code>组件定义</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Router, Route, hashHistory &#125; <span class="keyword">from</span> <span class="string">'react-router'</span>;</span><br><span class="line"></span><br><span class="line">render((</span><br><span class="line">  &lt;Router history=&#123;hashHistory&#125;&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"/"</span> component=&#123;App&#125;/&gt;</span><br><span class="line">  &lt;<span class="regexp">/Router&gt;</span></span><br><span class="line"><span class="regexp">), document.getElementById('app'));</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，用户访问根路由<code>/</code>，组件APP就会加载到<code>document.getElementById(&#39;app&#39;)</code></p>
</blockquote>
<ul>
<li><code>Router</code>组件有一个参数<code>history</code>，它的值<code>hashHistory</code>表示，路由的切换由<code>URL</code>的<code>hash</code>变化决定，即<code>URL</code>的<code>#</code>部分发生变化</li>
<li><code>Route</code>组件定义了<code>URL</code>路径与组件的对应关系。你可以同时使用多个<code>Route</code>组件</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Router history=&#123;hashHistory&#125;&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"/"</span> component=&#123;App&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"/repos"</span> component=&#123;Repos&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"/about"</span> component=&#123;About&#125;/&gt;</span><br><span class="line">&lt;<span class="regexp">/Router&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，用户访问<code>/repos</code>（比如<code>http://localhost:8080/#/repos</code>）时，加载<code>Repos</code>组件；访问<code>/about（http://localhost:8080/#/about）</code>时，加载<code>About</code>组件</p>
</blockquote>
<h3 id="8-2、嵌套路由"><a href="#8-2、嵌套路由" class="headerlink" title="8.2、嵌套路由"></a>8.2、嵌套路由</h3><blockquote>
<p><code>Route</code>组件还可以嵌套</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Router history=&#123;hashHistory&#125;&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"/"</span> component=&#123;App&#125;&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"/repos"</span> component=&#123;Repos&#125;/&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"/about"</span> component=&#123;About&#125;/&gt;</span><br><span class="line">  &lt;<span class="regexp">/Route&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>Router&gt;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，用户访问<code>/repos</code>时，会先加载<code>App</code>组件，然后在它的内部再加载<code>Repos</code>组件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;App&gt;</span><br><span class="line">  &lt;Repos/&gt;</span><br><span class="line">&lt;<span class="regexp">/App&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li><code>App</code>组件要写成下面的样子</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> React.createClass(&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">      &#123;this.props.children&#125;</span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>App</code>组件的<code>this.props.children</code>属性就是子组件</p>
</blockquote>
<h3 id="8-3、-path-属性"><a href="#8-3、-path-属性" class="headerlink" title="8.3、 path 属性"></a>8.3、 path 属性</h3><blockquote>
<p><code>Route</code>组件的<code>path</code>属性指定路由的匹配规则。这个属性是可以省略的，这样的话，不管路径是否匹配，总是会加载指定组件</p>
</blockquote>
<ul>
<li><code>Route</code>组件的<code>path</code>属性指定路由的匹配规则。这个属性是可以省略的，这样的话，不管路径是否匹配，总是会加载指定组件</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Route path=<span class="string">"inbox"</span> component=&#123;Inbox&#125;&gt;</span><br><span class="line">   &lt;Route path=<span class="string">"messages/:id"</span> component=&#123;Message&#125; /&gt;</span><br><span class="line">&lt;<span class="regexp">/Route&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>当用户访问<code>/inbox/messages/:id</code>时，会加载下面的组件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Inbox&gt;</span><br><span class="line">  &lt;Message/&gt;</span><br><span class="line">&lt;<span class="regexp">/Inbox&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果省略外层<code>Route</code>的<code>path</code>参数，写成下面的样子</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Route component=&#123;Inbox&#125;&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"inbox/messages/:id"</span> component=&#123;Message&#125; /&gt;</span><br><span class="line">&lt;<span class="regexp">/Route&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>现在用户访问<code>/inbox/messages/:id</code>时，组件加载还是原来的样子</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Inbox&gt;</span><br><span class="line">  &lt;Message/&gt;</span><br><span class="line">&lt;<span class="regexp">/Inbox&gt;</span></span><br></pre></td></tr></table></figure>
<h3 id="8-4、通配符"><a href="#8-4、通配符" class="headerlink" title="8.4、通配符"></a>8.4、通配符</h3><blockquote>
<p><code>path</code>属性可以使用通配符</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Route path=<span class="string">"/hello/:name"</span>&gt;</span><br><span class="line"><span class="comment">// 匹配 /hello/michael</span></span><br><span class="line"><span class="comment">// 匹配 /hello/ryan</span></span><br><span class="line"></span><br><span class="line">&lt;Route path=<span class="string">"/hello(/:name)"</span>&gt;</span><br><span class="line"><span class="comment">// 匹配 /hello</span></span><br><span class="line"><span class="comment">// 匹配 /hello/michael</span></span><br><span class="line"><span class="comment">// 匹配 /hello/ryan</span></span><br><span class="line"></span><br><span class="line">&lt;Route path=<span class="string">"/files/*.*"</span>&gt;</span><br><span class="line"><span class="comment">// 匹配 /files/hello.jpg</span></span><br><span class="line"><span class="comment">// 匹配 /files/hello.html</span></span><br><span class="line"></span><br><span class="line">&lt;Route path=<span class="string">"/files/*"</span>&gt;</span><br><span class="line"><span class="comment">// 匹配 /files/ </span></span><br><span class="line"><span class="comment">// 匹配 /files/a</span></span><br><span class="line"><span class="comment">// 匹配 /files/a/b</span></span><br><span class="line"></span><br><span class="line">&lt;Route path=<span class="string">"/**/*.jpg"</span>&gt;</span><br><span class="line"><span class="comment">// 匹配 /files/hello.jpg</span></span><br><span class="line"><span class="comment">// 匹配 /files/path/to/file.jpg</span></span><br></pre></td></tr></table></figure>
<p><strong>通配符的规则如下</strong></p>
<ul>
<li><strong>:paramName</strong></li>
</ul>
<blockquote>
<p><code>:paramName</code>匹配<code>URL</code>的一个部分，直到遇到下一个<code>/</code>、<code>?</code>、<code>#</code>为止。这个路径参数可以通过<code>this.props.params.paramName</code>取出</p>
</blockquote>
<ul>
<li><strong>()</strong></li>
</ul>
<blockquote>
<p><code>()</code>表示<code>URL</code>的这个部分是可选的</p>
</blockquote>
<ul>
<li>匹配任意字符，直到模式里面的下一个字符为止。匹配方式是非贪婪模式</li>
<li>匹配任意字符，直到下一个<code>/</code>、<code>?</code>、<code>#</code>为止。匹配方式是贪婪模式</li>
</ul>
<blockquote>
<p><code>path</code>属性也可以使用相对路径（不以<code>/</code>开头），匹配时就会相对于父组件的路径。嵌套路由如果想摆脱这个规则，可以使用绝对路由</p>
</blockquote>
<ul>
<li>此外，<code>URL</code>的查询字符串<code>/foo?bar=baz</code>，可以用<code>this.props.location.query.bar</code>获取</li>
</ul>
<h3 id="8-5、IndexRoute-组件"><a href="#8-5、IndexRoute-组件" class="headerlink" title="8.5、IndexRoute 组件"></a>8.5、IndexRoute 组件</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Router&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"/"</span> component=&#123;App&#125;&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"accounts"</span> component=&#123;Accounts&#125;/&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"statements"</span> component=&#123;Statements&#125;/&gt;</span><br><span class="line">  &lt;<span class="regexp">/Route&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>Router&gt;</span><br></pre></td></tr></table></figure>
<blockquote>
<ul>
<li>上面代码中，访问根路径<code>/</code>，不会加载任何子组件。也就是说，<code>App</code>组件的<code>this.props.children</code>，这时是<code>undefined</code></li>
<li>因此，通常会采用{<code>this.props.children</code> || <code>&lt;Home/&gt;}</code>这样的写法。这时，Home明明是<code>Accounts</code>和<code>Statements</code>的同级组件，却没有写在<code>Route</code>中</li>
<li>IndexRoute就是解决这个问题，显式指定Home是根路由的子组件，即指定默认情况下加载的子组件。你可以把<code>IndexRoute</code>想象成某个路径的<code>index.html</code></li>
</ul>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Router&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"/"</span> component=&#123;App&#125;&gt;</span><br><span class="line">    &lt;IndexRoute component=&#123;Home&#125;/&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"accounts"</span> component=&#123;Accounts&#125;/&gt;</span><br><span class="line">    &lt;Route path=<span class="string">"statements"</span> component=&#123;Statements&#125;/&gt;</span><br><span class="line">  &lt;<span class="regexp">/Route&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>Router&gt;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>现在，用户访问<code>/</code>的时候，加载的组件结构如下</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;App&gt;</span><br><span class="line">  &lt;Home/&gt;</span><br><span class="line">&lt;<span class="regexp">/App&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li><strong>注意</strong>，<code>IndexRoute</code>组件没有路径参数<code>path</code></li>
</ul>
<h3 id="8-6、Redirect-组件"><a href="#8-6、Redirect-组件" class="headerlink" title="8.6、Redirect 组件"></a>8.6、Redirect 组件</h3><blockquote>
<p><code>&lt;Redirect&gt;</code>组件用于路由的跳转，即用户访问一个路由，会自动跳转到另一个路由</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Route path=<span class="string">"inbox"</span> component=&#123;Inbox&#125;&gt;</span><br><span class="line">  &#123;<span class="comment">/* 从 /inbox/messages/:id 跳转到 /messages/:id */</span>&#125;</span><br><span class="line">  ＜Redirect <span class="keyword">from</span>=<span class="string">"messages/:id"</span> to=<span class="string">"/messages/:id"</span> /&gt;</span><br><span class="line">&lt;<span class="regexp">/Route&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>现在访问<code>/inbox/messages/5</code>，会自动跳转到<code>/messages/5</code></p>
</blockquote>
<h3 id="8-7、IndexRedirect-组件"><a href="#8-7、IndexRedirect-组件" class="headerlink" title="8.7、IndexRedirect 组件"></a>8.7、IndexRedirect 组件</h3><blockquote>
<p><code>IndexRedirect</code>组件用于访问根路由的时候，将用户重定向到某个子组件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Route path=<span class="string">"/"</span> component=&#123;App&#125;&gt;</span><br><span class="line">  ＜IndexRedirect to=<span class="string">"/welcome"</span> /&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"welcome"</span> component=&#123;Welcome&#125; /&gt;</span><br><span class="line">  &lt;Route path=<span class="string">"about"</span> component=&#123;About&#125; /&gt;</span><br><span class="line">&lt;<span class="regexp">/Route&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>用户访问根路径时，将自动重定向到子组件<code>welcome</code></p>
</blockquote>
<h3 id="8-8、Link"><a href="#8-8、Link" class="headerlink" title="8.8、Link"></a>8.8、Link</h3><blockquote>
<p><code>Link</code>组件用于取代<code>&lt;a&gt;</code>元素，生成一个链接，允许用户点击后跳转到另一个路由。它基本上就是<code>&lt;a&gt;</code>元素的<code>React</code> 版本，可以接收<code>Router</code>的状态</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line">    &lt;ul role="nav"&gt;</span><br><span class="line">      &lt;li&gt;&lt;Link to="/about"&gt;About&lt;/Link&gt;&lt;/li&gt;</span><br><span class="line">      &lt;li&gt;&lt;Link to="/repos"&gt;Repos&lt;/Link&gt;&lt;/li&gt;</span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果希望当前的路由与其他路由有不同样式，这时可以使用<code>Link</code>组件的<code>activeStyle</code>属性</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;Link to=<span class="string">"/about"</span> activeStyle=&#123;&#123;<span class="attr">color</span>: <span class="string">'red'</span>&#125;&#125;&gt;About&lt;<span class="regexp">/Link&gt;</span></span><br><span class="line"><span class="regexp">&lt;Link to="/</span>repos<span class="string">" activeStyle=&#123;&#123;color: 'red'&#125;&#125;&gt;Repos&lt;/Link&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li>在<code>Router</code>组件之外，导航到路由页面，可以使用浏览器的<code>History API</code>，像下面这样写</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; browserHistory &#125; <span class="keyword">from</span> <span class="string">'react-router'</span>;</span><br><span class="line">browserHistory.push(<span class="string">'/some/path'</span>);</span><br></pre></td></tr></table></figure>
<h3 id="8-9、IndexLink"><a href="#8-9、IndexLink" class="headerlink" title="8.9、IndexLink"></a>8.9、IndexLink</h3><blockquote>
<p>如果链接到根路由<code>/</code>，不要使用<code>Link</code>组件，而要使用<code>IndexLink</code>组件</p>
</blockquote>
<ul>
<li>是因为对于根路由来说，<code>activeStyle</code>和<code>activeClassName</code>会失效，或者说总是生效，因为<code>/</code>会匹配任何子路由。而<code>IndexLink</code>组件会使用路径的精确匹配</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;IndexLink to=<span class="string">"/"</span> activeClassName=<span class="string">"active"</span>&gt;</span><br><span class="line">  Home</span><br><span class="line">&lt;<span class="regexp">/IndexLink&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，根路由只会在精确匹配时，才具有<code>activeClassName</code></p>
</blockquote>
<h3 id="8-10、histroy-属性"><a href="#8-10、histroy-属性" class="headerlink" title="8.10、histroy 属性"></a>8.10、histroy 属性</h3><blockquote>
<p><code>Router</code>组件的<code>history</code>属性，用来监听浏览器地址栏的变化，并将<code>URL</code>解析成一个地址对象，供 <code>React Router</code> 匹配</p>
</blockquote>
<ul>
<li><code>history</code>属性，一共可以设置三种值。<ul>
<li><code>browserHistory</code></li>
<li><code>hashHistory</code></li>
<li><code>createMemoryHistory</code></li>
</ul>
</li>
</ul>
<blockquote>
<p>如果设为<code>hashHistory</code>，路由将通过<code>URL</code>的hash部分<code>（#）</code>切换，<code>URL</code>的形式类似<code>example.com/#/some/path</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; hashHistory &#125; <span class="keyword">from</span> <span class="string">'react-router'</span></span><br><span class="line"></span><br><span class="line">render(</span><br><span class="line">  &lt;Router history=&#123;hashHistory&#125; routes=&#123;routes&#125; /&gt;,</span><br><span class="line">  <span class="built_in">document</span>.getElementById(<span class="string">'app'</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果设为<code>browserHistory</code>，浏览器的路由就不再通过<code>Hash</code>完成了，而显示正常的路径<code>example.com/some/path</code>，背后调用的是浏览器的<code>History API</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; browserHistory &#125; <span class="keyword">from</span> <span class="string">'react-router'</span></span><br><span class="line"></span><br><span class="line">render(</span><br><span class="line">  &lt;Router history=&#123;browserHistory&#125; routes=&#123;routes&#125; /&gt;,</span><br><span class="line">  <span class="built_in">document</span>.getElementById(<span class="string">'app'</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>但是，这种情况需要对服务器改造。否则用户直接向服务器请求某个子路由，会显示网页找不到的<code>404</code>错误。</p>
</blockquote>
<h3 id="8-11、表单处理"><a href="#8-11、表单处理" class="headerlink" title="8.11、表单处理"></a>8.11、表单处理</h3><blockquote>
<p><code>Link</code>组件用于正常的用户点击跳转，但是有时还需要表单跳转、点击按钮跳转等操作</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&lt;form onSubmit=&#123;<span class="keyword">this</span>.handleSubmit&#125;&gt;</span><br><span class="line">  &lt;input type=<span class="string">"text"</span> placeholder=<span class="string">"userName"</span>/&gt;</span><br><span class="line">  &lt;input type=<span class="string">"text"</span> placeholder=<span class="string">"repo"</span>/&gt;</span><br><span class="line">  &lt;button type=<span class="string">"submit"</span>&gt;Go&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>form&gt;</span><br></pre></td></tr></table></figure>
<p><strong>第一种方法是使用browserHistory.push</strong></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; browserHistory &#125; <span class="keyword">from</span> <span class="string">'react-router'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">  handleSubmit(event) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">const</span> userName = event.target.elements[<span class="number">0</span>].value</span><br><span class="line">    <span class="keyword">const</span> repo = event.target.elements[<span class="number">1</span>].value</span><br><span class="line">    <span class="keyword">const</span> path = <span class="string">`/repos/<span class="subst">$&#123;userName&#125;</span>/<span class="subst">$&#123;repo&#125;</span>`</span></span><br><span class="line">    browserHistory.push(path)</span><br><span class="line">  &#125;,</span><br></pre></td></tr></table></figure>
<p><strong>第二种方法是使用context对象</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">export default React.createClass(&#123;</span><br><span class="line"></span><br><span class="line">  // ask for `router` from context</span><br><span class="line">  contextTypes: &#123;</span><br><span class="line">    router: React.PropTypes.object</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  handleSubmit(event) &#123;</span><br><span class="line">    // ...</span><br><span class="line">    this.context.router.push(path)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<h3 id="8-12、路由的钩子"><a href="#8-12、路由的钩子" class="headerlink" title="8.12、路由的钩子"></a>8.12、路由的钩子</h3><blockquote>
<p>每个路由都有<code>Enter</code>和<code>Leave</code>钩子，用户进入或离开该路由时触发</p>
</blockquote>
<ul>
<li>上面的代码中，如果用户离开<code>/messages/:id</code>，进入<code>/about</code>时，会依次触发以下的钩子<ul>
<li><code>/messages/:id</code>的<code>onLeave</code></li>
<li><code>/inbox</code>的<code>onLeave</code></li>
<li><code>/about</code>的<code>onEnter</code></li>
</ul>
</li>
</ul>
<h2 id="九、redux"><a href="#九、redux" class="headerlink" title="九、redux"></a>九、redux</h2><h3 id="9-1-Redux-的适用场景"><a href="#9-1-Redux-的适用场景" class="headerlink" title="9.1 Redux 的适用场景"></a>9.1 Redux 的适用场景</h3><ul>
<li>某个组件的状态，需要共享</li>
<li>某个状态需要在任何地方都可以拿到</li>
<li>一个组件需要改变全局状态</li>
<li>一个组件需要改变另一个组件的状态</li>
</ul>
<p><strong>Redux设计思想</strong></p>
<blockquote>
<p><code>Redux</code> 的设计思想很简单，就两句话</p>
</blockquote>
<ul>
<li><code>Web</code> 应用是一个状态机，视图与状态是一一对应的</li>
<li>所有的状态，保存在一个对象里面</li>
</ul>
<h3 id="9-2-基本概念和-API"><a href="#9-2-基本概念和-API" class="headerlink" title="9.2 基本概念和 API"></a>9.2 基本概念和 API</h3><p><strong>Store</strong></p>
<ul>
<li><code>Store</code> 提供了三个方法<ul>
<li><code>store.getState()</code></li>
<li><code>store.dispatch()</code></li>
<li><code>store.subscribe()</code></li>
</ul>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">let</span> &#123; subscribe, dispatch, getState &#125; = createStore(reducer);</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>Store</code>就是保存数据的地方，你可以把它看成一个容器。整个应用只能有一个 <code>Store</code></p>
</blockquote>
<ul>
<li><code>Redux</code> 提供<code>createStore</code>这个函数，用来生成 <code>Store</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">const</span> store = createStore(reducer); <span class="comment">// 返回新生成的 Store 对象</span></span><br></pre></td></tr></table></figure>
<p><strong>State</strong></p>
<blockquote>
<p><code>Store</code>对象包含所有数据。如果想得到某个时点的数据，就要对 <code>Store</code> 生成快照。这种时点的数据集合，就叫做 <code>State</code></p>
</blockquote>
<ul>
<li>当前时刻的 <code>State</code>，可以通过<code>store.getState()</code>拿到</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">const</span> store = createStore(reducer);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> state = store.getState();</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>Redux</code> 规定， 一个 <code>State</code> 对应一个 <code>View</code>。只要 <code>State</code> 相同，<code>View</code> 就相同。你知道 <code>State</code>，就知道 <code>View</code> 是什么样，反之亦然</p>
</blockquote>
<p><strong>Action</strong></p>
<blockquote>
<p><code>State</code> 的变化，会导致 <code>View</code> 的变化。但是，用户接触不到 <code>State</code>，只能接触到 <code>View</code>。所以，<code>State</code> 的变化必须是 <code>View</code> 导致的。<code>Action</code> 就是 <code>View</code> 发出的通知，表示 <code>State</code> 应该要发生变化了</p>
</blockquote>
<ul>
<li><code>Action</code> 是一个对象。其中的<code>type</code>属性是必须的，表示 <code>Action</code> 的名称。其他属性可以自由设置</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> action = &#123;</span><br><span class="line">  type: <span class="string">'ADD_TODO'</span>,</span><br><span class="line">  payload: <span class="string">'Learn Redux'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，<code>Action</code> 的名称是<code>ADD_TODO</code>，它携带的信息是字符串<code>Learn Redux</code></li>
<li>可以这样理解，<code>Action</code> 描述当前发生的事情。改变 <code>State</code> 的唯一办法，就是使用 <code>Action</code>。它会运送数据到 <code>Store</code></li>
</ul>
<blockquote>
<p><code>action</code>有两个作用，一个是定义我们的应用可以进行的动作或操作的类型，另一个是传递改变应用状态的数据。在<code>Redux</code>的约定中，<code>action</code>只有<code>type</code>属性是必须包含的，其他的数据如何定义全在于你想要如何使用，当然如果你希望你定义的<code>action</code>能够规范一些的话，也可以遵从Flux Standard Action的标准</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="comment">// action 类型</span></span><br><span class="line">  type: <span class="string">'INCREMENT'</span>,</span><br><span class="line">  <span class="comment">// payload 中返回我们要传递的数据，用来修改应用 state</span></span><br><span class="line">  payload: &#123;</span><br><span class="line">    num: <span class="number">1</span>  </span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// payload 数据未获取成功时返回 true</span></span><br><span class="line">  error: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 一些不必要在 payload 中传递的其他数据</span></span><br><span class="line">  meta: &#123;</span><br><span class="line">    success: <span class="literal">true</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>Action Creator</strong></p>
<blockquote>
<p><code>View</code> 要发送多少种消息，就会有多少种 <code>Action</code>。如果都手写，会很麻烦。可以定义一个函数来生成 <code>Action</code>，这个函数就叫 <code>Action Creator</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> ADD_TODO = <span class="string">'添加 TODO'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addTodo</span>(<span class="params">text</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    type: ADD_TODO,</span><br><span class="line">    text</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> action = addTodo(<span class="string">'Learn Redux'</span>);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>addTodo</code>函数就是一个 <code>Action Creator</code></p>
</blockquote>
<p><strong>store.dispatch()</strong></p>
<blockquote>
<p><code>store.dispatch()</code>是 <code>View</code> 发出 <code>Action</code> 的唯一方法</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">const</span> store = createStore(fn);</span><br><span class="line"></span><br><span class="line">store.dispatch(&#123;</span><br><span class="line">  type: <span class="string">'ADD_TODO'</span>,</span><br><span class="line">  payload: <span class="string">'Learn Redux'</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>store.dispatch</code>接受一个 <code>Action</code> 对象作为参数，将它发送出去</p>
</blockquote>
<ul>
<li>结合 <code>Action Creator</code>，这段代码可以改写如下</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">store.dispatch(addTodo(<span class="string">'Learn Redux'</span>));</span><br></pre></td></tr></table></figure>
<p><strong>Reducer</strong></p>
<blockquote>
<p><code>Store</code> 收到 <code>Action</code> 以后，必须给出一个新的 <code>State</code>，这样 <code>View</code> 才会发生变化。这种 <code>State</code> 的计算过程就叫做 <code>Reducer</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> reducer = <span class="function"><span class="keyword">function</span> (<span class="params">state, action</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="keyword">return</span> new_state;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<ul>
<li>整个应用的初始状态，可以作为 <code>State</code> 的默认值。下面是一个实际的例子</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> defaultState = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">const</span> reducer = <span class="function">(<span class="params">state = defaultState, action</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'ADD'</span>:</span><br><span class="line">      <span class="keyword">return</span> state + action.payload;</span><br><span class="line">    <span class="keyword">default</span>: </span><br><span class="line">      <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> state = reducer(<span class="number">1</span>, &#123;</span><br><span class="line">  type: <span class="string">'ADD'</span>,</span><br><span class="line">  payload: <span class="number">2</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>reducer</code>函数收到名为<code>ADD</code>的 <code>Action</code> 以后，就返回一个新的 <code>State</code>，作为加法的计算结果。其他运算的逻辑（比如减法），也可以根据 <code>Action</code> 的不同来实现</p>
</blockquote>
<ul>
<li>实际应用中，<code>Reducer</code> 函数不用像上面这样手动调用，<code>store.dispatch</code>方法会触发 <code>Reducer</code> 的自动执行</li>
<li>为此，<code>Store</code> 需要知道 <code>Reducer</code> 函数，做法就是在生成 <code>Store</code> 的时候，将 <code>Reducer</code> 传入<code>createStore</code>方法</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">import &#123; createStore &#125; from &apos;redux&apos;;</span><br><span class="line">const store = createStore(reducer);</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，<code>createStore</code>接受 <code>Reducer</code> 作为参数，生成一个新的 <code>Store</code>。以后每当<code>store.dispatch</code>发送过来一个新的 <code>Action</code>，就会自动调用 <code>Reducer</code>，得到新的 <code>State</code></li>
<li>为什么这个函数叫做 <code>Reducer</code>呢？因为它可以作为数组的<code>reduce</code>方法的参数</li>
</ul>
<p><strong>纯函数</strong></p>
<ul>
<li><code>Reducer</code> 函数最重要的特征是，它是一个纯函数。也就是说，只要是同样的输入，必定得到同样的输出</li>
<li>纯函数是函数式编程的概念，必须遵守以下一些约束<ul>
<li>不得改写参数</li>
<li>不能调用系统 <code>I/O</code> 的<code>API</code></li>
<li>不能调用<code>Date.now()</code>或者<code>Math.random()</code>等不纯的方法，因为每次会得到不一样的结果</li>
</ul>
</li>
</ul>
<blockquote>
<p>由于 <code>Reducer</code> 是纯函数，就可以保证同样的<code>State</code>，必定得到同样的 <code>View</code>。但也正因为这一点，<code>Reducer</code> 函数里面不能改变 <code>State</code>，必须返回一个全新的对象，请参考下面的写法</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// State 是一个对象</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reducer</span>(<span class="params">state, action</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.assign(&#123;&#125;, state, &#123; thingToChange &#125;);</span><br><span class="line">  <span class="comment">// 或者</span></span><br><span class="line">  <span class="keyword">return</span> &#123; ...state, ...newState &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// State 是一个数组</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reducer</span>(<span class="params">state, action</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> [...state, newItem];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>最好把 <code>State</code> 对象设成只读。你没法改变它，要得到新的 <code>State</code>，唯一办法就是生成一个新对象。这样的好处是，任何时候，与某个 <code>View</code> 对应的 <code>State</code> 总是一个不变的对象</p>
</blockquote>
<p><strong>store.subscribe()</strong></p>
<blockquote>
<p><code>Store</code> 允许使用<code>store.subscribe</code>方法设置监听函数，一旦 <code>State</code> 发生变化，就自动执行这个函数</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">const</span> store = createStore(reducer);</span><br><span class="line"></span><br><span class="line">store.subscribe(listener);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>显然，只要把 <code>View</code> 的更新函数（对于 <code>React</code> 项目，就是组件的<code>render</code>方法或<code>setState</code>方法）放入<code>listen</code>，就会实现 <code>View</code> 的自动渲染</p>
</blockquote>
<ul>
<li><code>store.subscribe</code>方法返回一个函数，调用这个函数就可以解除监听</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> unsubscribe = store.subscribe(<span class="function"><span class="params">()</span> =&gt;</span></span><br><span class="line">  <span class="built_in">console</span>.log(store.getState())</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">unsubscribe();</span><br></pre></td></tr></table></figure>
<h3 id="9-3-Reducer-的拆分"><a href="#9-3-Reducer-的拆分" class="headerlink" title="9.3 Reducer 的拆分"></a>9.3 Reducer 的拆分</h3><blockquote>
<p><code>Reducer</code> 函数负责生成 <code>State</code>。由于整个应用只有一个 <code>State</code> 对象，包含所有数据，对于大型应用来说，这个 State 必然十分庞大，导致 <code>Reducer</code> 函数也十分庞大</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> chatReducer = <span class="function">(<span class="params">state = defaultState, action = &#123;&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; type, payload &#125; = action;</span><br><span class="line">  <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">    <span class="keyword">case</span> ADD_CHAT:</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Object</span>.assign(&#123;&#125;, state, &#123;</span><br><span class="line">        chatLog: state.chatLog.concat(payload)</span><br><span class="line">      &#125;);</span><br><span class="line">    <span class="keyword">case</span> CHANGE_STATUS:</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Object</span>.assign(&#123;&#125;, state, &#123;</span><br><span class="line">        statusMessage: payload</span><br><span class="line">      &#125;);</span><br><span class="line">    <span class="keyword">case</span> CHANGE_USERNAME:</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Object</span>.assign(&#123;&#125;, state, &#123;</span><br><span class="line">        userName: payload</span><br><span class="line">      &#125;);</span><br><span class="line">    <span class="keyword">default</span>: <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> chatReducer = <span class="function">(<span class="params">state = defaultState, action = &#123;&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    chatLog: chatLog(state.chatLog, action),</span><br><span class="line">    statusMessage: statusMessage(state.statusMessage, action),</span><br><span class="line">    userName: userName(state.userName, action)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，<code>Reducer</code> 函数被拆成了三个小函数，每一个负责生成对应的属</li>
<li>这样一拆，<code>Reducer</code> 就易读易写多了。而且，这种拆分与 <code>React</code> 应用的结构相吻合:一个 <code>React</code> 根组件由很多子组件构成。这就是说，子组件与子 <code>Reducer</code> 完全可以对应</li>
</ul>
<blockquote>
<p><code>Redux</code> 提供了一个<code>combineReducers</code>方法，用于 <code>Reducer</code> 的拆分。你只要定义各个子 <code>Reducer</code> 函数，然后用这个方法，将它们合成一个大的 <code>Reducer</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; combineReducers &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> chatReducer = combineReducers(&#123;</span><br><span class="line">  chatLog,</span><br><span class="line">  statusMessage,</span><br><span class="line">  userName</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> todoApp;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这种写法有一个前提，就是 State 的属性名必须与子 Reducer 同名。如果不同名，就要采用下面的写法</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reducer</span>(<span class="params">state = &#123;&#125;, action</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    a: doSomethingWithA(state.a, action),</span><br><span class="line">    b: processB(state.b, action),</span><br><span class="line">    c: c(state.c, action)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>总之，<code>combineReducers()</code>做的就是产生一个整体的 Reducer 函数。该函数根据 <code>State</code> 的 <code>key</code> 去执行相应的子 <code>Reducer</code>，并将返回结果合并成一个大的 <code>State</code> 对象</p>
</blockquote>
<ul>
<li>你可以把所有子 <code>Reducer</code> 放在一个文件里面，然后统一引入</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; combineReducers &#125; <span class="keyword">from</span> <span class="string">'redux'</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> reducers <span class="keyword">from</span> <span class="string">'./reducers'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> reducer = combineReducers(reducers)</span><br></pre></td></tr></table></figure>
<h3 id="9-4-工作流程"><a href="#9-4-工作流程" class="headerlink" title="9.4 工作流程"></a>9.4 工作流程</h3><p><img src="https://poetries1.gitee.io/img-repo/2019/10/478.png" alt><br><img src="https://poetries1.gitee.io/img-repo/2019/10/479.png" alt><br><img src="https://poetries1.gitee.io/img-repo/2019/10/480.png" alt><br><img src="https://poetries1.gitee.io/img-repo/2019/10/481.png" alt><br><img src="https://poetries1.gitee.io/img-repo/2019/10/482.png" alt></p>
<ul>
<li>首先，用户发出 <code>Action</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">store.dispatch(action);</span><br></pre></td></tr></table></figure>
<ul>
<li>然后，<code>Store</code> 自动调用 <code>Reducer</code>，并且传入两个参数：当前 <code>State</code> 和收到的 <code>Action</code>。 <code>Reducer</code> 会返回新的 <code>State</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> nextState = todoApp(previousState, action);</span><br></pre></td></tr></table></figure>
<ul>
<li><code>State</code> 一旦有变化，<code>Store</code> 就会调用监听函数</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 设置监听函数</span></span><br><span class="line">store.subscribe(listener);</span><br></pre></td></tr></table></figure>
<ul>
<li><code>listener</code>可以通过<code>store.getState()</code>得到当前状态。如果使用的是 <code>React</code>，这时可以触发重新渲染 <code>View</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">listerner</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> newState = store.getState();</span><br><span class="line">  component.setState(newState);   </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="9-5-实例：计数器"><a href="#9-5-实例：计数器" class="headerlink" title="9.5 实例：计数器"></a>9.5 实例：计数器</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Counter = <span class="function">(<span class="params">&#123; value, onIncrement, onDecrement &#125;</span>) =&gt;</span> (</span><br><span class="line">  &lt;div&gt;</span><br><span class="line">  &lt;h1&gt;&#123;value&#125;&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">  &lt;button onClick=&#123;onIncrement&#125;&gt;+&lt;/</span>button&gt;</span><br><span class="line">  &lt;button onClick=&#123;onDecrement&#125;&gt;-&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>div&gt;</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> reducer = <span class="function">(<span class="params">state = <span class="number">0</span>, action</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'INCREMENT'</span>: <span class="keyword">return</span> state + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'DECREMENT'</span>: <span class="keyword">return</span> state - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">default</span>: <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = createStore(reducer);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> render = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  ReactDOM.render(</span><br><span class="line">    &lt;Counter</span><br><span class="line">      value=&#123;store.getState()&#125;</span><br><span class="line">      onIncrement=&#123;() =&gt; store.dispatch(&#123;<span class="attr">type</span>: <span class="string">'INCREMENT'</span>&#125;)&#125;</span><br><span class="line">      onDecrement=&#123;() =&gt; store.dispatch(&#123;<span class="attr">type</span>: <span class="string">'DECREMENT'</span>&#125;)&#125;</span><br><span class="line">    /&gt;,</span><br><span class="line">    <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">render();</span><br><span class="line">store.subscribe(render);</span><br></pre></td></tr></table></figure>
<h2 id="十、中间件与异步操作"><a href="#十、中间件与异步操作" class="headerlink" title="十、中间件与异步操作"></a>十、中间件与异步操作</h2><blockquote>
<p><code>Redux</code> 的基本做法：用户发出 <code>Action</code>，<code>Reducer</code> 函数算出新的 <code>State</code>，<code>View</code> 重新渲染</p>
</blockquote>
<ul>
<li><p>一个关键问题没有解决：异步操作怎么办？<code>Action</code> 发出以后，<code>Reducer</code> 立即算出 <code>State</code>，这叫做同步；<code>Action</code> 发出以后，过一段时间再执行 <code>Reducer</code>，这就是异步</p>
</li>
<li><p>怎么才能 <code>Reducer</code> 在异步操作结束后自动执行呢？这就要用到新的工具：中间件（<code>middleware</code>）</p>
</li>
</ul>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/477.png" alt></p>
<h3 id="10-1-中间件的概念"><a href="#10-1-中间件的概念" class="headerlink" title="10.1 中间件的概念"></a>10.1 中间件的概念</h3><blockquote>
<p>中间件就是一个函数，对<code>store.dispatch</code>方法进行了改造，在发出 <code>Action</code> 和执行 <code>Reducer</code> 这两步之间，添加了其他功能。</p>
</blockquote>
<h3 id="10-2-中间件的用法"><a href="#10-2-中间件的用法" class="headerlink" title="10.2 中间件的用法"></a>10.2 中间件的用法</h3><blockquote>
<p>常用的中间件都有现成的，只要引用别人写好的模块即可。比如日志中间件，就有现成的<code>redux-logger</code>模块</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; applyMiddleware, createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">import</span> createLogger <span class="keyword">from</span> <span class="string">'redux-logger'</span>;</span><br><span class="line"><span class="keyword">const</span> logger = createLogger();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = createStore(</span><br><span class="line">  reducer,</span><br><span class="line">  applyMiddleware(logger)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>redux-logger</code>提供一个生成器<code>createLogger</code>，可以生成日志中间件<code>logger</code>。然后，将它放在applyMiddleware方法之中，传入<code>createStore</code>方法，就完成了<code>store.dispatch()</code>的功能增强</p>
</blockquote>
<p><strong>这里有两点需要注意</strong></p>
<ul>
<li>（1）<code>createStore</code>方法可以接受整个应用的初始状态作为参数，那样的话，<code>applyMiddleware</code>就是第三个参数了</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> store = createStore(</span><br><span class="line">  reducer,</span><br><span class="line">  initial_state,</span><br><span class="line">  applyMiddleware(logger)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<ul>
<li>（2）中间件的次序有讲究</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> store = createStore(</span><br><span class="line">  reducer,</span><br><span class="line">  applyMiddleware(thunk, promise, logger)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>applyMiddleware</code>方法的三个参数，就是三个中间件。有的中间件有次序要求，使用前要查一下文档。比如，<code>logger</code>就一定要放在最后，否则输出结果会不正确</p>
</blockquote>
<h3 id="10-3、applyMiddlewares"><a href="#10-3、applyMiddlewares" class="headerlink" title="10.3、applyMiddlewares()"></a>10.3、applyMiddlewares()</h3><blockquote>
<p><code>applyMiddlewares</code>这个方法。它是 <code>Redux</code> 的原生方法，作用是将所有中间件组成一个数组，依次执行</p>
</blockquote>
<h3 id="10-4-异步操作的基本思路"><a href="#10-4-异步操作的基本思路" class="headerlink" title="10.4 异步操作的基本思路"></a>10.4 异步操作的基本思路</h3><blockquote>
<p>理解了中间件以后，就可以处理异步操作了</p>
</blockquote>
<ul>
<li>同步操作只要发出一种 <code>Action</code> 即可，异步操作的差别是它要发出三种 <code>Action</code><ul>
<li>操作发起时的 <code>Action</code></li>
<li>操作成功时的 <code>Action</code></li>
<li>操作失败时的 <code>Action</code></li>
</ul>
</li>
</ul>
<blockquote>
<p>以向服务器取出数据为例，三种 <code>Action</code> 可以有两种不同的写法</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 写法一：名称相同，参数不同</span></span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">'FETCH_POSTS'</span> &#125;</span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">'FETCH_POSTS'</span>, <span class="attr">status</span>: <span class="string">'error'</span>, <span class="attr">error</span>: <span class="string">'Oops'</span> &#125;</span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">'FETCH_POSTS'</span>, <span class="attr">status</span>: <span class="string">'success'</span>, <span class="attr">response</span>: &#123; ... &#125; &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法二：名称不同</span></span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">'FETCH_POSTS_REQUEST'</span> &#125;</span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">'FETCH_POSTS_FAILURE'</span>, <span class="attr">error</span>: <span class="string">'Oops'</span> &#125;</span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">'FETCH_POSTS_SUCCESS'</span>, <span class="attr">response</span>: &#123; ... &#125; &#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>除了 <code>Action</code>种类不同，异步操作的 <code>State</code> 也要进行改造，反映不同的操作状态。下面是 <code>State</code> 的一个例子</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> state = &#123;</span><br><span class="line">  <span class="comment">// ... </span></span><br><span class="line">  isFetching: <span class="literal">true</span>,</span><br><span class="line">  didInvalidate: <span class="literal">true</span>,</span><br><span class="line">  lastUpdated: <span class="string">'xxxxxxx'</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>State</code> 的属性<code>isFetching</code>表示是否在抓取数据。<code>didInvalidate</code>表示数据是否过时，<code>lastUpdated</code>表示上一次更新时间</p>
</blockquote>
<blockquote>
<p>现在，整个异步操作的思路就很清楚了</p>
</blockquote>
<ul>
<li>操作开始时，送出一个 <code>Action</code>，触发 <code>State</code> 更新为”正在操作”状态，<code>View</code> 重新渲染</li>
<li>操作结束后，再送出一个 <code>Action</code>，触发 <code>State</code> 更新为”操作结束”状态，<code>View</code> 再一次重新渲染</li>
</ul>
<h3 id="10-5-redux-thunk-中间件"><a href="#10-5-redux-thunk-中间件" class="headerlink" title="10.5 redux-thunk 中间件"></a>10.5 redux-thunk 中间件</h3><blockquote>
<p>异步操作至少要送出两个 <code>Action</code>：用户触发第一个 <code>Action</code>，这个跟同步操作一样，没有问题；如何才能在操作结束时，系统自动送出第二个 <code>Action</code> 呢</p>
</blockquote>
<ul>
<li>奥妙就在 <code>Action Creator</code> 之中</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AsyncApp</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  componentDidMount() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; dispatch, selectedPost &#125; = <span class="keyword">this</span>.props</span><br><span class="line">    dispatch(fetchPosts(selectedPost))</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码是一个异步组件的例子。加载成功后（<code>componentDidMount</code>方法），它送出了（<code>dispatch</code>方法）一个 <code>Action</code>，向服务器要求数据 <code>fetchPosts(selectedSubreddit)</code>。这里的<code>fetchPosts</code>就是 <code>Action Creator</code></p>
</blockquote>
<ul>
<li>下面就是<code>fetchPosts</code>的代码，关键之处就在里面</li>
</ul>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/476.png" alt></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fetchPosts = <span class="function"><span class="params">postTitle</span> =&gt;</span> <span class="function">(<span class="params">dispatch, getState</span>) =&gt;</span> &#123;</span><br><span class="line">  dispatch(requestPosts(postTitle));</span><br><span class="line">  <span class="keyword">return</span> fetch(<span class="string">`/some/API/<span class="subst">$&#123;postTitle&#125;</span>.json`</span>)</span><br><span class="line">    .then(<span class="function"><span class="params">response</span> =&gt;</span> response.json())</span><br><span class="line">    .then(<span class="function"><span class="params">json</span> =&gt;</span> dispatch(receivePosts(postTitle, json)));</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用方法一</span></span><br><span class="line">store.dispatch(fetchPosts(<span class="string">'reactjs'</span>));</span><br><span class="line"><span class="comment">// 使用方法二</span></span><br><span class="line">store.dispatch(fetchPosts(<span class="string">'reactjs'</span>)).then(<span class="function"><span class="params">()</span> =&gt;</span></span><br><span class="line">  <span class="built_in">console</span>.log(store.getState())</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>fetchPosts</code>是一个<code>Action Creator</code>（动作生成器），返回一个函数。这个函数执行后，先发出一个<code>Action（requestPosts(postTitle)</code>），然后进行异步操作。拿到结果后，先将结果转成 <code>JSON</code> 格式，然后再发出一个 <code>Action（ receivePosts(postTitle, json)</code>）</p>
</blockquote>
<p><strong>上面代码中，有几个地方需要注意</strong></p>
<ul>
<li><code>fetchPosts</code>返回了一个函数，而普通的 <code>Action Creator</code> 默认返回一个对象</li>
<li>返回的函数的参数是<code>dispatch</code>和<code>getState</code>这两个 <code>Redux</code>方法，普通的<code>Action Creator</code>的参数是 <code>Action</code> 的内容</li>
<li>在返回的函数之中，先发出一个 <code>Action（requestPosts(postTitle)）</code>，表示操作开始</li>
<li>异步操作结束之后，再发出一个 <code>Action（receivePosts(postTitle, json)）</code>，表示操作结束</li>
</ul>
<blockquote>
<p>这样的处理，就解决了自动发送第二个 <code>Action</code> 的问题。但是，又带来了一个新的问题，<code>Action</code> 是由<code>store.dispatch</code>方法发送的。而<code>store.dispatch</code>方法正常情况下，参数只能是对象，不能是函数</p>
</blockquote>
<ul>
<li>这时，就要使用中间件<code>redux-thunk</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore, applyMiddleware &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">import</span> thunk <span class="keyword">from</span> <span class="string">'redux-thunk'</span>;</span><br><span class="line"><span class="keyword">import</span> reducer <span class="keyword">from</span> <span class="string">'./reducers'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Note: this API requires redux@&gt;=3.1.0</span></span><br><span class="line"><span class="keyword">const</span> store = createStore(</span><br><span class="line">  reducer,</span><br><span class="line">  applyMiddleware(thunk)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码使用<code>redux-thunk</code>中间件，改造<code>store.dispatch</code>，使得后者可以接受函数作为参数</li>
</ul>
<p><strong>因此，异步操作的第一种解决方案就是，写出一个返回函数的 <code>Action Creator</code>，然后使用<code>redux-thunk</code>中间件改造<code>store.dispatch</code></strong></p>
<h3 id="10-6、redux-promise-中间件"><a href="#10-6、redux-promise-中间件" class="headerlink" title="10.6、redux-promise 中间件"></a>10.6、redux-promise 中间件</h3><hr>
<blockquote>
<p>既然 <code>Action Creator</code> 可以返回函数，当然也可以返回其他值。另一种异步操作的解决方案，就是让 <code>Action Creator</code> 返回一个 <code>Promise</code> 对象</p>
</blockquote>
<ul>
<li>这就需要使用<code>redux-promise</code>中间件</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createStore, applyMiddleware &#125; <span class="keyword">from</span> <span class="string">'redux'</span>;</span><br><span class="line"><span class="keyword">import</span> promiseMiddleware <span class="keyword">from</span> <span class="string">'redux-promise'</span>;</span><br><span class="line"><span class="keyword">import</span> reducer <span class="keyword">from</span> <span class="string">'./reducers'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = createStore(</span><br><span class="line">  reducer,</span><br><span class="line">  applyMiddleware(promiseMiddleware)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这个中间件使得<code>store.dispatch</code>方法可以接受 <code>Promise</code> 对象作为参数。这时，<code>Action Creator</code> 有两种写法</p>
</blockquote>
<ul>
<li>写法一，返回值是一个 <code>Promise</code> 对象</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fetchPosts = </span><br><span class="line">  (dispatch, postTitle) =&gt; <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">     dispatch(requestPosts(postTitle));</span><br><span class="line">     <span class="keyword">return</span> fetch(<span class="string">`/some/API/<span class="subst">$&#123;postTitle&#125;</span>.json`</span>)</span><br><span class="line">       .then(<span class="function"><span class="params">response</span> =&gt;</span> &#123;</span><br><span class="line">         type: <span class="string">'FETCH_POSTS'</span>,</span><br><span class="line">         payload: response.json()</span><br><span class="line">       &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<ul>
<li>写法二，<code>Action</code> 对象的<code>payload</code>属性是一个 <code>Promise</code> 对象。这需要从<code>redux-actions</code>模块引入<code>createAction</code>方法，并且写法也要变成下面这样</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createAction &#125; <span class="keyword">from</span> <span class="string">'redux-actions'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AsyncApp</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  componentDidMount() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; dispatch, selectedPost &#125; = <span class="keyword">this</span>.props</span><br><span class="line">    <span class="comment">// 发出同步 Action</span></span><br><span class="line">    dispatch(requestPosts(selectedPost));</span><br><span class="line">    <span class="comment">// 发出异步 Action</span></span><br><span class="line">    dispatch(createAction(</span><br><span class="line">      <span class="string">'FETCH_POSTS'</span>, </span><br><span class="line">      fetch(<span class="string">`/some/API/<span class="subst">$&#123;postTitle&#125;</span>.json`</span>)</span><br><span class="line">        .then(<span class="function"><span class="params">response</span> =&gt;</span> response.json())</span><br><span class="line">    ));</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，第二个<code>dispatch</code>方法发出的是异步 <code>Action</code>，只有等到操作结束，这个 <code>Action</code> 才会实际发出</li>
<li>注意，<code>createAction</code>的第二个参数必须是一个 <code>Promise</code> 对象</li>
</ul>
<h2 id="十一、react-redux"><a href="#十一、react-redux" class="headerlink" title="十一、react-redux"></a>十一、react-redux</h2><blockquote>
<ul>
<li>为了方便使用，<code>Redux</code> 的作者封装了一个 <code>React</code>专用的库 <code>React-Redux</code></li>
<li>这个库是可以选用的。实际项目中，你应该权衡一下，是直接使用 <code>Redux</code>，还是使用 <code>React-Redux</code>。后者虽然提供了便利，但是需要掌握额外的 <code>API</code>，并且要遵守它的组件拆分规范</li>
</ul>
</blockquote>
<h3 id="11-1-UI-组件"><a href="#11-1-UI-组件" class="headerlink" title="11.1 UI 组件"></a>11.1 UI 组件</h3><blockquote>
<p><code>React-Redux</code> 将所有组件分成两大类：<code>UI</code> 组件（<code>presentational component</code>）和容器组件（<code>container component</code>）</p>
</blockquote>
<p><strong>UI 组件有以下几个特征</strong></p>
<ul>
<li>只负责 <code>UI</code> 的呈现，不带有任何业务逻辑</li>
<li>没有状态（即不使用<code>this.state</code>这个变量）</li>
<li>所有数据都由参数（<code>this.props</code>）提供</li>
<li>不使用任何 <code>Redux</code> 的 <code>API</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 例子</span></span><br><span class="line"><span class="keyword">const</span> Title =</span><br><span class="line">  value =&gt; <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>&#123;value&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>因为不含有状态，<code>UI</code> 组件又称为”纯组件”，即它纯函数一样，纯粹由参数决定它的值</p>
</blockquote>
<h3 id="11-2、容器组件"><a href="#11-2、容器组件" class="headerlink" title="11.2、容器组件"></a>11.2、容器组件</h3><ul>
<li>负责管理数据和业务逻辑，不负责 <code>UI</code> 的呈现</li>
<li>带有内部状态</li>
<li>使用 <code>Redux</code> 的 <code>API</code></li>
</ul>
<p><strong><code>UI</code> 组件负责 <code>UI</code> 的呈现，容器组件负责管理数据和逻辑</strong></p>
<blockquote>
<p>如果一个组件既有 <code>UI</code> 又有业务逻辑，那怎么办？回答是，将它拆分成下面的结构：外面是一个容器组件，里面包了一个<code>UI</code> 组件。前者负责与外部的通信，将数据传给后者，由后者渲染出视图</p>
</blockquote>
<ul>
<li><code>React-Redux</code> 规定，所有的 <code>UI</code> 组件都由用户提供，容器组件则是由 <code>React-Redux</code> 自动生成。也就是说，用户负责视觉层，状态管理则是全部交给它</li>
</ul>
<h3 id="11-3、connect"><a href="#11-3、connect" class="headerlink" title="11.3、connect()"></a>11.3、connect()</h3><blockquote>
<p><code>React-Redux</code> 提供<code>connect</code>方法，用于从 <code>UI</code> 组件生成容器组件。<code>connect</code>的意思，就是将这两种组件连起来。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"><span class="keyword">const</span> VisibleTodoList = connect()(TodoList);</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，<code>TodoList</code>是 <code>UI</code> 组件，<code>VisibleTodoList</code>就是由 <code>React-Redux</code> 通过<code>connect</code>方法自动生成的容器组件</li>
</ul>
<blockquote>
<p>但是，因为没有定义业务逻辑，上面这个容器组件毫无意义，只是 <code>UI</code> 组件的一个单纯的包装层。为了定义业务逻辑，需要给出下面两方面的信息。</p>
</blockquote>
<ul>
<li>（1）输入逻辑：外部的数据（即<code>state</code>对象）如何转换为 <code>UI</code> 组件的参数</li>
<li>（2）输出逻辑：用户发出的动作如何变为 <code>Action</code> 对象，从 <code>UI</code> 组件传出去</li>
</ul>
<blockquote>
<p>因此，<code>connect</code>方法的完整 <code>API</code> 如下</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> VisibleTodoList = connect(</span><br><span class="line">  mapStateToProps,</span><br><span class="line">  mapDispatchToProps</span><br><span class="line">)(TodoList)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面代码中，<code>connect</code>方法接受两个参数<code>：mapStateToProps</code>和<code>mapDispatchToProps</code>。它们定义了 <code>UI</code> 组件的业务逻辑。前者负责输入逻辑，即将<code>state</code>映射到 <code>UI</code> 组件的参数（<code>props</code>），后者负责输出逻辑，即将用户对 <code>UI</code> 组件的操作映射成 <code>Action</code></p>
</blockquote>
<h3 id="11-4、mapStateToProps"><a href="#11-4、mapStateToProps" class="headerlink" title="11.4、mapStateToProps()"></a>11.4、mapStateToProps()</h3><blockquote>
<p><code>mapStateToProps</code>是一个函数。它的作用就是像它的名字那样，建立一个从（外部的）<code>state</code>对象到（<code>UI</code> 组件的）<code>props</code>对象的映射关系</p>
</blockquote>
<ul>
<li>作为函数，<code>mapStateToProps</code>执行后应该返回一个对象，里面的每一个键值对就是一个映射</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mapStateToProps = <span class="function">(<span class="params">state</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    todos: getVisibleTodos(state.todos, state.visibilityFilter)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，<code>mapStateToProps</code>是一个函数，它接受<code>state</code>作为参数，返回一个对象</li>
<li>这个对象有一个<code>todos</code>属性，代表 <code>UI</code> 组件的同名参数，后面的<code>getVisibleTodos</code>也是一个函数，可以从<code>state</code>算出 <code>todos</code> 的值</li>
</ul>
<blockquote>
<p>下面就是<code>getVisibleTodos</code>的一个例子，用来算出<code>todos</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> getVisibleTodos = <span class="function">(<span class="params">todos, filter</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">switch</span> (filter) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'SHOW_ALL'</span>:</span><br><span class="line">      <span class="keyword">return</span> todos</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'SHOW_COMPLETED'</span>:</span><br><span class="line">      <span class="keyword">return</span> todos.filter(<span class="function"><span class="params">t</span> =&gt;</span> t.completed)</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'SHOW_ACTIVE'</span>:</span><br><span class="line">      <span class="keyword">return</span> todos.filter(<span class="function"><span class="params">t</span> =&gt;</span> !t.completed)</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Unknown filter: '</span> + filter)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><code>mapStateToProps</code>会订阅 <code>Store</code>，每当<code>state</code>更新的时候，就会自动执行，重新计算 <code>UI</code> 组件的参数，从而触发 <code>UI</code> 组件的重新渲染</li>
<li><code>mapStateToProps</code>的第一个参数总是<code>state</code>对象，还可以使用第二个参数，代表容器组件的<code>props</code>对象</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 容器组件的代码</span></span><br><span class="line"><span class="comment">//    &lt;FilterLink filter="SHOW_ALL"&gt;</span></span><br><span class="line"><span class="comment">//      All</span></span><br><span class="line"><span class="comment">//    &lt;/FilterLink&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> mapStateToProps = <span class="function">(<span class="params">state, ownProps</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    active: ownProps.filter === state.visibilityFilter</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>使用<code>ownProps</code>作为参数后，如果容器组件的参数发生变化，也会引发 <code>UI</code> 组件重新渲染</p>
</blockquote>
<ul>
<li><code>connect</code>方法可以省略<code>mapStateToProps</code>参数，那样的话，<code>UI</code> 组件就不会订阅<code>Store</code>，就是说 <code>Store</code> 的更新不会引起 <code>UI</code> 组件的更新</li>
</ul>
<h3 id="11-5、mapDispatchToProps"><a href="#11-5、mapDispatchToProps" class="headerlink" title="11.5、mapDispatchToProps()"></a>11.5、mapDispatchToProps()</h3><blockquote>
<p><code>mapDispatchToProps</code>是<code>connect</code>函数的第二个参数，用来建立 <code>UI</code> 组件的参数到<code>store.dispatch</code>方法的映射</p>
</blockquote>
<ul>
<li>也就是说，它定义了哪些用户的操作应该当作 <code>Action</code>，传给 <code>Store</code>。它可以是一个函数，也可以是一个对象</li>
<li>如果<code>mapDispatchToProps</code>是一个函数，会得到<code>dispatch</code>和<code>ownProps</code>（容器组件的<code>props</code>对象）两个参数</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mapDispatchToProps = (</span><br><span class="line">  dispatch,</span><br><span class="line">  ownProps</span><br><span class="line">) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    onClick: <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      dispatch(&#123;</span><br><span class="line">        type: <span class="string">'SET_VISIBILITY_FILTER'</span>,</span><br><span class="line">        filter: ownProps.filter</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>从上面代码可以看到，<code>mapDispatchToProps</code>作为函数，应该返回一个对象，该对象的每个键值对都是一个映射，定义了 <code>UI</code> 组件的参数怎样发出 <code>Action</code></li>
<li>如果<code>mapDispatchToProps</code>是一个对象，它的每个键名也是对应 <code>UI</code> 组件的同名参数，键值应该是一个函数，会被当作 <code>Action creator</code> ，返回的 <code>Action</code> 会由 <code>Redux</code> 自动发出。举例来说，上面的<code>mapDispatchToProps</code>写成对象就是下面这样</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mapDispatchToProps = &#123;</span><br><span class="line">  onClick: <span class="function">(<span class="params">filter</span>) =&gt;</span> &#123;</span><br><span class="line">    type: <span class="string">'SET_VISIBILITY_FILTER'</span>,</span><br><span class="line">    filter: filter</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="11-6、-组件"><a href="#11-6、-组件" class="headerlink" title="11.6、 组件"></a>11.6、<provider> 组件</provider></h3><blockquote>
<p><code>connect</code>方法生成容器组件以后，需要让容器组件拿到<code>state</code>对象，才能生成 <code>UI</code> 组件的参数</p>
</blockquote>
<blockquote>
<p>一种解决方法是将<code>state</code>对象作为参数，传入容器组件。但是，这样做比较麻烦，尤其是容器组件可能在很深的层级，一级级将<code>state</code>传下去就很麻烦。</p>
</blockquote>
<ul>
<li><code>React-Redux</code> 提供<code>Provider</code>组件，可以让容器组件拿到<code>state</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Provider &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span></span><br><span class="line"><span class="keyword">import</span> todoApp <span class="keyword">from</span> <span class="string">'./reducers'</span></span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./components/App'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> store = createStore(todoApp);</span><br><span class="line"></span><br><span class="line">render(</span><br><span class="line">  &lt;Provider store=&#123;store&#125;&gt;</span><br><span class="line">    &lt;App /&gt;</span><br><span class="line">  &lt;<span class="regexp">/Provider&gt;,</span></span><br><span class="line"><span class="regexp">  document.getElementById('root')</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>
<ul>
<li>上面代码中，<code>Provider</code>在根组件外面包了一层，这样一来，<code>App</code>的所有子组件就默认都可以拿到<code>state</code></li>
<li>它的原理是<code>React</code>组件的<code>context</code>属性</li>
</ul>
<h3 id="11-7、实例：计数器"><a href="#11-7、实例：计数器" class="headerlink" title="11.7、实例：计数器"></a>11.7、实例：计数器</h3><blockquote>
<p>我们来看一个实例。下面是一个计数器组件，它是一个纯的 <code>UI</code> 组件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Counter</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; value, onIncreaseClick &#125; = <span class="keyword">this</span>.props</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;span&gt;&#123;value&#125;&lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">        &lt;button onClick=&#123;onIncreaseClick&#125;&gt;Increase&lt;/</span>button&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>这个 <code>UI</code> 组件有两个参数：<code>value</code>和<code>onIncreaseClick</code>。前者需要从<code>state</code>计算得到，后者需要向外发出 <code>Action</code></p>
</blockquote>
<ul>
<li>接着，定义<code>value</code>到<code>state</code>的映射，以及<code>onIncreaseClick</code>到<code>dispatch</code>的映射</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapStateToProps</span>(<span class="params">state</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    value: state.count</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapDispatchToProps</span>(<span class="params">dispatch</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    onIncreaseClick: <span class="function"><span class="params">()</span> =&gt;</span> dispatch(increaseAction)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Action Creator</span></span><br><span class="line"><span class="keyword">const</span> increaseAction = &#123; <span class="attr">type</span>: <span class="string">'increase'</span> &#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>然后，使用<code>connect</code>方法生成容器组件</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">const App = connect(</span><br><span class="line">  mapStateToProps,</span><br><span class="line">  mapDispatchToProps</span><br><span class="line">)(Counter)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>然后，定义这个组件的 <code>Reducer</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Reducer</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">counter</span>(<span class="params">state = &#123; count: <span class="number">0</span> &#125;, action</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> count = state.count</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">'increase'</span>:</span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">count</span>: count + <span class="number">1</span> &#125;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="keyword">return</span> state</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>最后，生成<code>store</code>对象，并使用<code>Provider</code>在根组件外面包一层</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, &#123; Component &#125; <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> PropTypes <span class="keyword">from</span> <span class="string">'prop-types'</span></span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">'react-dom'</span></span><br><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span></span><br><span class="line"><span class="keyword">import</span> &#123; Provider, connect &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// React component</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Counter</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; value, onIncreaseClick &#125; = <span class="keyword">this</span>.props</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;span&gt;&#123;value&#125;&lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">        &lt;button onClick=&#123;onIncreaseClick&#125;&gt;Increase&lt;/</span>button&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">Counter.propTypes = &#123;</span></span><br><span class="line"><span class="regexp">  value: PropTypes.number.isRequired,</span></span><br><span class="line"><span class="regexp">  onIncreaseClick: PropTypes.func.isRequired</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ Action</span></span><br><span class="line"><span class="regexp">const increaseAction = &#123; type: 'increase' &#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ Reducer</span></span><br><span class="line"><span class="regexp">function counter(state = &#123; count: 0 &#125;, action) &#123;</span></span><br><span class="line"><span class="regexp">  const count = state.count</span></span><br><span class="line"><span class="regexp">  switch (action.type) &#123;</span></span><br><span class="line"><span class="regexp">    case 'increase':</span></span><br><span class="line"><span class="regexp">      return &#123; count: count + 1 &#125;</span></span><br><span class="line"><span class="regexp">    default:</span></span><br><span class="line"><span class="regexp">      return state</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ Store</span></span><br><span class="line"><span class="regexp">const store = createStore(counter)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ Map Redux state to component props</span></span><br><span class="line"><span class="regexp">function mapStateToProps(state) &#123;</span></span><br><span class="line"><span class="regexp">  return &#123;</span></span><br><span class="line"><span class="regexp">    value: state.count</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ Map Redux actions to component props</span></span><br><span class="line"><span class="regexp">function mapDispatchToProps(dispatch) &#123;</span></span><br><span class="line"><span class="regexp">  return &#123;</span></span><br><span class="line"><span class="regexp">    onIncreaseClick: () =&gt; dispatch(increaseAction)</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ Connected Component</span></span><br><span class="line"><span class="regexp">const App = connect(</span></span><br><span class="line"><span class="regexp">  mapStateToProps,</span></span><br><span class="line"><span class="regexp">  mapDispatchToProps</span></span><br><span class="line"><span class="regexp">)(Counter)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">ReactDOM.render(</span></span><br><span class="line"><span class="regexp">  &lt;Provider store=&#123;store&#125;&gt;</span></span><br><span class="line"><span class="regexp">    &lt;App /</span>&gt;</span><br><span class="line">  &lt;<span class="regexp">/Provider&gt;,</span></span><br><span class="line"><span class="regexp">  document.getElementById('root')</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>
<h2 id="十二、思维导图总结"><a href="#十二、思维导图总结" class="headerlink" title="十二、思维导图总结"></a>十二、思维导图总结</h2><p><img src="https://poetries1.gitee.io/img-repo/2019/10/483.png" alt></p>

      </div>
    
  </div>

</article>

<button class="assist-btn2 circle" id="assist_btn2" title="点亮屏幕" style="left: 27px; top: 152px;">
  <i class="iconfont" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>
<button class="assist-btn1 circle" id="assist_btn1" title="关闭屏幕亮度" style="left: 27px; top: 152px;">
  <i class="iconfont toc-title" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>


<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>	

<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
<script>
  const btw = new BTWPlugin();
  btw.init({
    id: "container",
    blogId: "22699-1592137983091-414",
    name: "前端进阶之旅",
    qrcode: "https://poetries1.gitee.io/img-repo/2020/06/qrcode.jpg",
    keyword: "3a3b3c",
  });
</script>

<script type="text/javascript">

// white theme
var body = {color: "#555", background: "#000"};
var a_tag = {color: "#222"};
var header = { background: "#222"};
var logo_line_i = {background: "#222"};
// var post_code = {background: "#eee", color: "#222"};

function switch_theme() {
 $("body").css(body);
 $("a:not('.links-of-author-item a, .site-state-item a, .site-state-posts a, .feed-link a, .motion-element a, .post-tags a, .show-commit-cls a, #donate_board a')").css(a_tag);
 $(".header, .footer").css(header);
 $(".logo-line-before i, .logo-line-after i").css(logo_line_i);
 //$(".post code").css(post_code);
 $("#idhyt-surprise-ball #idhyt-surprise-ball-animation .drag").css(a_tag);
 $(".post-title-link, .posts-expand .post-meta, .post-comments-count, .disqus-comment-count, .post-category a, .post-nav-next a, .post-nav-item a").css(a_tag);
 
 // $("code").css({color: '#c5c8c6', background: '#1d1f21'});
 //$("#assist_btn1").hide(1500);
}

$(function () {
$("#assist_btn2").css("display","none");
 $("#assist_btn1").click(function() {
     switch_theme();
$("div#toc.toc-article").css({
 "background":"#eaeaea",
 "opacity":1
});
$(".toc-article ol").show();
$("#toc.toc-article .toc-title").css("color","#a98602");
$("#assist_btn1").css("display","none");
$("#assist_btn2").css("display","block");
 });
$("#assist_btn2").click(function() {
$("#assist_btn2").css("display","none");
$("#assist_btn1").css("display","block");
$("body").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
     $(".header, .footer").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
$(".toc-article ol").toggle(1000);
 });
});


//背景随机

var Y, O, E, L, B, C, T, z, N, S, A, I;
!function() {
var e = function() {
for (O.clearRect(0, 0, L, B), T = [{
x: 0,
y: .7 * B + C
}, {
x: 0,
y: .7 * B - C
}]; T[1].x < L + C;) t(T[0], T[1])
}, t = function(e, t) {
O.beginPath(), O.moveTo(e.x, e.y), O.lineTo(t.x, t.y);
var n = t.x + (2 * I() - .25) * C,
 r = a(t.y);
O.lineTo(n, r), O.closePath(), N -= S / -50, O.fillStyle = "#" + (127 * A(N) + 128 << 16 | 127 * A(N + S / 3) + 128 << 8 | 127 * A(N + S / 3 * 2) + 128).toString(16), O.fill(), T[0] = T[1], T[1] = {
 x: n,
 y: r
}
}, a = function n(e) {
var t = e + (2 * I() - 1.1) * C;
return t > B || t < 0 ? n(e) : t
};
Y = document.getElementById("evanyou"), O = Y.getContext("2d"), E = window.devicePixelRatio || 1, L = window.innerWidth, B = window.innerHeight, C = 90, z = Math, N = 0, S = 2 * z.PI, A = z.cos, I = z.random, Y.width = L * E, Y.height = B * E, O.scale(E, E), O.globalAlpha = .6, document.onclick = e, document.ontouchstart = e, e()
}()

   
$("#toc-eye").click(function(){
$("#toc.toc-article").toggle(1000);
});

</script>


   
  <div class="text-center donation">
    <div class="inner-donation">
      <span class="btn-donation">支持一下</span>
      <div class="donation-body">
        <div class="tip text-center">扫一扫，支持poetries</div>
        <ul>
        
          <li class="item">
            
              <span>微信扫一扫</span>
            
            <img src="/images/weixin.jpg" alt="">
          </li>
        
          <li class="item">
            
              <span>支付宝扫一扫</span>
            
            <img src="/images/zhifubao.jpg" alt="">
          </li>
        
        </ul>
      </div>
    </div>
  </div>


   
  <div class="box-prev-next clearfix">
    <a class="show pull-left" href="/2017/11/07/js-some-filter-reduce/">
        <i class="icon icon-angle-left"></i>
    </a>
    <a class="show pull-right" href="/2017/11/07/es-in-react/">
        <i class="icon icon-angle-right"></i>
    </a>
  </div>




</div>


  <a id="backTop" class="back-top">
    <i class="icon-angle-up"></i>
  </a>




  <div class="modal" id="modal">
  <span id="cover" class="cover hide"></span>
  <div id="modal-dialog" class="modal-dialog hide-dialog">
    <div class="modal-header">
      <span id="close" class="btn-close">关闭</span>
    </div>
    <hr>
    <div class="modal-body">
      <ul class="list-toolbox">
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/archives/"
              rel="noopener noreferrer"
              target="_self"
              >
              博客
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/categories/"
              rel="noopener noreferrer"
              target="_self"
              >
              分类
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/tags/"
              rel="noopener noreferrer"
              target="_self"
              >
              标签
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/search/"
              rel="noopener noreferrer"
              target="_self"
              >
              搜索
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/link/"
              rel="noopener noreferrer"
              target="_self"
              >
              友链
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/about/"
              rel="noopener noreferrer"
              target="_self"
              >
              关于
            </a>
          </li>
        
      </ul>

    </div>
  </div>
</div>



  
      <div class="fexo-comments comments-post">
    

    

    
    

    

    
    

    

<!-- Gitalk评论插件通用代码 -->
<div id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script>
const gitalk = new Gitalk({
  clientID: '5567a2c4abb858009d96',
  clientSecret: 'b9039ec056cf5c2346b3cdb63308a28c163f91e5',
  repo: 'poetries.github.io',
  owner: 'poetries',
  // 在这里设置一下截取前50个字符串, 这是因为 github 对 label 的长度有了要求, 如果超过
  // 50个字符串则会报错.
  // id: location.pathname.split('/').pop().substring(0, 49),
  id: location.pathname,
  admin: ['poetries'],
  // facebook-like distraction free mode
  distractionFreeMode: false
})
gitalk.render('gitalk-container')
</script>
<!-- Gitalk代码结束 -->



  </div>

  

  <script type="text/javascript">
  function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //IE
      script.onreadystatechange = function() {
        if (script.readyState == 'loaded' ||
          script.readyState == 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else { //Others
      script.onload = function() {
        callback();
      };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  window.onload = function() {
    loadScript('/js/bundle.js?235683', function() {
      // load success
    });
  }
</script>


  <!-- 页面点击小红心 -->
  <script type="text/javascript" src="/js/clicklove.js"></script>
 
  
</body>
</html>
